Python: class attributes and instance attributes

I am new to python and found out that class attributes are like static data members in C ++. However, I was embarrassed by trying the following code:

>>> class Foo: ... a=1 ... >>> f1=Foo(); >>> f2=Foo() >>> f1.a 1 >>> f1.a=5 >>> f1.a 5 >>> f2.a 1 

Shouldn't f2.a also be 5?

If a is defined as a list instead of an integer, expected behavior:

 >>> class Foo: ... a=[] ... >>> f1=Foo(); >>> f2=Foo() >>> f1.a [] >>> f1.a.append(5) >>> f1.a [5] >>> f2.a [5] 

I looked at Python: the difference between class and instance attributes , but it does not answer my question.

Can anyone explain why the difference is? Thanks

+6
source share
6 answers

You do not do the same in your second example. In the first example, you assign f1.a new value:

 f1.a = 5 

In the second example, you simply expand the list:

 f1.a.append(5) 

This does not affect what f1.a indicates. If you did this:

 f1.a = [5] 

You will find that this behaves the same as your first example.

But consider this example:

 >>> f1=Foo() >>> f2=Foo() >>> Foo.a = 5 >>> f1.a 5 >>> f2.a 5 

In this example, we are actually changing the value of the class attribute, and the change is displayed in all instances of the class. When are you Type:

 f1.a = 5 

You override the class attribute with the instance attribute.

+7
source

Python class attributes and object attributes are stored in separate dictionaries . For the object f1 they can be accessed through, respectively, f1.__class__.__dict__ and f1.__dict__ . Doing print f1.__class__ is Foo will print True .

When you reference an attribute of an object, Python first tries to find it in the object dictionary. If he does not find it there, he checks the dictionary of classes (and so on by heredity).

When you assign f1.a , you add an entry to the object dictionary for f1 . Subsequent searches for f1.a will find this entry. Search f2.a will still find the class attribute - an entry in the class attribute dictionary.

You can return f1.a to 1 by deleting it:

 del f1.a 

This will delete the entry for a in the object dictionary f1 , and subsequent searches will continue in the class dictionary. So, after that print f1.a 1.

+9
source
 >>> class Foo: ... a=1 ... >>> f1=Foo() >>> f2=Foo() >>> f1.a # no instance attribute here in f1, so look up class attribute in Foo 1 >>> f1.a=5 # create new instance attribute in f1 >>> f1.a # instance attribute here. Great, let use this. 5 >>> f2.a # no instance attribute in f2, look up class attribute in Foo 1 >>> >>> class Foo: ... a=[] ... >>> f1=Foo() >>> f2=Foo() >>> f1.a # no instance attribute - look up class attribute in Foo [] >>> f1.a.append(5) # no instance attribute, modify class attribute in-place >>> f1.a # no instance attribute - look up class attribute in Foo [5] >>> f2.a # same here [5] 
+5
source

My advice: to understand such cases, run the tests with id and __dict__ too:

 class Foo: a=1 # Foo.a == 1 # id(Foo.a) == 10021840 # Foo.__dict__ == {'a': 1, '__module__': '__main__', '__doc__': None} f1 = Foo() # f1.a == 1 # id(f1.a) == 10021840 # f1.__dict__ == {} f2 = Foo() # f2.a == 1 # id(f2.a) == 10021840 # f2.__dict__ == {} f1.a = 5 # f1.a == 5 # id(f1.a) == 10021792 # f1.__dict__ == {'a': 5} # f2.a == 1 # id(f2.a) == 10021840 # f2.__dict__ == {} 

This shows that until the f1.a = 5 command was executed, the f1 instance does not have a personal attribute a .

Then, why did the print f1.a executed before f1.a = 5 create 1 ?
It's because:

An instance of the class has a namespace implemented as a dictionary, which is the first place in which the search for attribute references is performed. When an attribute is not found there, and the instance class has an attribute by that name, the search continues with the attribute class.

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

+3
source

The following happens:

When you instantiate a new object f1 = Foo() , it has no attributes of its own. Whenever you try to access, for example, f1.a , you are redirected to the Foo.a classes:

 print f1.__dict__ {} print f1.a 1 

However, if you set f1.a = 5 , the instance will receive a new attribute of this value:

 print f1.__dict__ {'a': 5} 

The class definition is not affected by this, like any other instances.

In the second example, you are not reassigning anything. With append you only use the very list that was defined in the class. Thus, your instance still refers to this list, like all other instances.

0
source

When you assign an attribute in python, it does not matter where this attribute can already be defined, the new assignment always applies to the assigned object. When you say

 >>> f1.a=5 

The object that has the attribute here is an instance, so the instance gets a new value.

0
source

Source: https://habr.com/ru/post/898023/


All Articles