Why is adding attributes to an already instantiated object allowed?
A leading principle is that there is no such thing as a declaration. That is, you never declare "this class has a method foo" or "instances of this class have an attribute bar", let alone making a statement about the types of objects to be stored there. You simply define a method, attribute, class, etc. and it's added. As JBernardo points out, any __init__
method does the very same thing. It wouldn't make a lot of sense to arbitrarily restrict creation of new attributes to methods with the name __init__
. And it's sometimes useful to store a function as __init__
which don't actually have that name (e.g. decorators), and such a restriction would break that.
Now, this isn't universally true. Builtin types omit this capability as an optimization. Via __slots__
, you can also prevent this on user-defined classes. But this is merely a space optimization (no need for a dictionary for every object), not a correctness thing.
If you want a safety net, well, too bad. Python does not offer one, and you cannot reasonably add one, and most importantly, it would be shunned by Python programmers who embrace the language (read: almost all of those you want to work with). Testing and discipline, still go a long way to ensuring correctness. Don't use the liberty to make up attributes outside of __init__
if it can be avoided, and do automated testing. I very rarely have an AttributeError
or a logical error due to trickery like this, and of those that happen, almost all are caught by tests.
what's the meaning of creating new variables at a class instance?
Here is a link to a similar question with lots of helpful answers: Why is adding attributes to an already instantiated object allowed?
Now for my answer. Python is a dynamic language meaning that things can be changed at run time for execution. But what is important to realize is that the answer to your question is more a matter of style and opinion.
Instantiating your class with all the needed variables inside of it gives you the benefit of encapsulation and the safety of knowing that every time the class is instantiated that you will have access to that variable. On the other hand adding a variable after instantiation may give you different benefits in specific use cases.
How is the initialization of x.a is considered in this code?
Python is a Dynamically Typed Language. Which means you can set the value of any variable without having to declare that variable.
In your code snippet, x = Node(50)
is creating a new node class object with x.left
assigned to 50.
Now by doing x.a = Node(40)
, we are just defining a new attribute of the object x
with name a
, which will be a Node class object with left
value of 50.
If you do dir(x)
it will give you something like this,
['__class__',
'__delattr__',
#Some other attributes
'__weakref__',
'a',
'data',
'left',
'right']
If you take a look at the last 4 attributes you can see that a
is now also added as an attribute of the x object.
Conclution
if you do x.new_attribute = Node(10,20,30)
when new_attribute
isn't present in the object then python will just create a new attribute inside the x
object with the name new_attribute
& assign it the value of Node(10,20,30)
.
How can I create an object and add attributes to it?
You could use my ancient Bunch recipe, but if you don't want to make a "bunch class", a very simple one already exists in Python -- all functions can have arbitrary attributes (including lambda functions). So, the following works:
obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')
Whether the loss of clarity compared to the venerable Bunch
recipe is OK, is a style decision I will of course leave up to you.
Why is the list in the class attribute still a class attribute after instantiation, not an instance attribute?
In your case property = []
is a class variable, which is shared with all instances of your class. I asume, that you don't want to share the value of property = []
with other classes. That means you need a instance variable, which can be defined like down below.
class MyClass:
def __init__(self):
self.property = []
def append(self, value):
self.property.append(value)
This should give you your expected output, sheers!
Prevent creating new attributes outside __init__
I wouldn't use __dict__
directly, but you can add a function to explicitly "freeze" a instance:
class FrozenClass(object):
__isfrozen = False
def __setattr__(self, key, value):
if self.__isfrozen and not hasattr(self, key):
raise TypeError( "%r is a frozen class" % self )
object.__setattr__(self, key, value)
def _freeze(self):
self.__isfrozen = True
class Test(FrozenClass):
def __init__(self):
self.x = 42#
self.y = 2**3
self._freeze() # no new attributes after this point.
a,b = Test(), Test()
a.x = 10
b.z = 10 # fails
Related Topics
Type Object 'Datetime.Datetime' Has No Attribute 'Datetime'
Manifest.In Ignored on "Python Setup.Py Install" - No Data Files Installed
Access Memory Address in Python
Installing Scipy in Python 3.5 on 32-Bit Windows 7 MAChine
Remove Reverse Duplicates from Dataframe
How to Assign a Value to a Tensorflow Variable
Pandas - Explanation on Apply Function Being Slow
How to Escape Latex Code Received Through User Input
Set Environment Variable in Python Script
How to Get Rid of Beautifulsoup User Warning
Differencebetween I = I + 1 and I += 1 in a 'For' Loop
How to Escape Curly-Brackets in F-Strings
Convert Timedelta64[Ns] Column to Seconds in Python Pandas Dataframe
Python: Mocking a Context Manager
Skip Multiple Iterations in Loop