New class instance with non-None class attribute?

I have a Python class that has a class attribute set to something other than None . When you create a new instance, the changes made to this attribute are saved in all instances.

Here is some code to figure this out:

  class Foo(object): a = [] b = 2 foo = Foo() foo.a.append('item') foo.b = 5 

Using foo.a returns ['item'] and foo.b returns 5 , as you would expect.

When I create a new instance (let's call it bar ), using bar.a returns ['item'] and bar.b return 5 too! However, when I initially set all the attributes of the None class, then set them to __init__ , for example:

  class Foo(object): a = None b = None def __init__(self): self.a = [] self.b = 2 

Using bar.a returns [] and bar.b returns 2 , and foo.a returns ['item'] and foo.b returns 5 .

How it works? I apparently never encountered this problem for 3 years when I was programming Python, and would like some clarification. I also cannot find it anywhere in the documentation, so providing me with a link would be great if that was possible. :)

+4
source share
4 answers

Yes, that’s how it should work.

If a and b belong to an instance of Foo , then the correct way to do this is:

 class Foo(object): def __init__(self): self.a = [] self.b = 2 

The following makes a and b belong to the class itself, so all instances have the same variables:

 class Foo(object): a = [] b = 2 

When you mix two methods - as in the second example, this does not add anything useful and just causes confusion.

One point worth mentioning is that when you do the following in your first example:

 foo.b = 5 

you do not change Foo.b , you add a new attribute Foo , which is the "shadow" of Foo.b When you do this, neither bar.b nor Foo.b will change. If you subsequently execute del foo.b , this will remove this attribute, and Foo.b refers to Foo.b .

+7
source

Yes, this is exactly what you should expect. When you define a class variable, these attributes are bound to the class, not to the instance. You may not always notice that when you assign an attribute to an instance, the instance gets a new value, masking the class attribute. Methods that change in place, such as list.append , will not give the instance a new attribute, because they simply modify the existing object, which turns out to be an attribute of the class.

Each time each instance of the class must have its own unique value for the attribute, you should usually set it in the __init__ method to ensure that it is different for each instance.

only if the class has an attribute that has a reasonable default value, and that value has an immutable type (which cannot be changed in place), for example int or str , if you set the attributes to the class.

+1
source

Yes, that seems awesome at the beginning, but this is how python works, as said in other answers. Three years of python without its reduction? You are lucky, very lucky. If I were you, I would check the sensitive code of the past for small animals ...

Be careful with function calls: def func(list=[]) has similar behavior, which may be unexpected.

And, if possible, include parsing in your commit commits. Or at least run pylint in your code, it is very instructive and will probably tell you about this trick (this is not a trick, in fact, it is very logical and orthogonal, just a python path).

+1
source

You are misleading a class attribute with an instance attribute. In your first example, there are only class attributes a and b , but in the second you also have instance attributes with the same name.

It looks like Python will pass attribute references from the instance to the class if the instance attribute is missing (today I learned something new!).

You may find this code instructive:

 class Foo: a = 1 b = 2 def __init__(self): self.a=3 print Foo.a # => 1 print Foo().a # => 3 print Foo.b # => 2 print Foo().b # => 2 
0
source

All Articles