Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What in the world is the attribute "__class__" in python

Tags:

python

class

I have a question about __class__ in python.

The documentation says that __class__ is the class to which a class instance belongs. So I conducted a series of experiments:

class counter:
    count = 0
    def __init__(self):
            self.__class__.count += 1

NewCounter1 = counter()
print NewCounter1.count   #The result is 1
NewCounter2 = counter()
print NewCounter2.count   #The result is 2
print NewCounter2.__class__.count is NewCounter2.count  #result: True

Everything goes well.

Then I enter code as follows:

NewCounter2.__class__.count = 3

print NewCounter1.count                    #result:3
print NewCounter1.__class__.count      #result:3
print NewCounter2.count                    #result:3
print NewCounter2.__class__.count      #result:3
print NewCounter2.__class__.count is NewCounter2.count      #result: True

From the code above, I thought that maybe NewCounter1.count equals NewCounter1, or __class__.count, but the following code surprised me:

NewCounter2.count = 5

print NewCounter1.count                 #result:3
print NewCounter1.__class__.count   #result:3
print NewCounter2.count                 #result:5
print NewCounter2.__class__.count   #result:3
print NewCounter2.__class__.count is NewCounter2.count       #result: False

Why has NewCounter2.count changed but NewCounter2.__class__.count remained at 3? What's more, when I changed NewCounter2.count, NewCounter2.__class__.count is NewCounter2.count became False. What in the world is the attribute __class__

like image 487
Searene Avatar asked Nov 09 '11 05:11

Searene


People also ask

What is __ class __ in Python?

__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .

What is an attribute in a class Python?

Python class attributes are variables of a class that are shared between all of its instances. They differ from instance attributes in that instance attributes are owned by one specific instance of the class only, and are not shared between instances.

What is attribute in Python?

Attributes of a class are function objects that define corresponding methods of its instances. They are used to implement access controls of the classes. Attributes of a class can also be accessed using the following built-in methods and functions : getattr() – This function is used to access the attribute of object.

What is attribute Python example?

Class attributes are the variables defined directly in the class that are shared by all objects of the class. Instance attributes are attributes or properties attached to an instance of a class. Instance attributes are defined in the constructor.


3 Answers

"From codes above, I thought that maybe NewCounter1.count equals NewCounter1. _class_.count"

The problem is that at the moment of this sentence in your question, after the only instructions:

NewCounter1 = counter()
NewCounter2 = counter()
NewCounter2.__class__.count = 3

having created NewCounter1 and NewCounter2
and having modified the class attribute counter.count,
there are no objects NewCounter1.count nor NewCounter2.count in existence, and then "equals" has no real meaning.

.

See the creation of NewCounter1 and just after:

class counter:
    count = 0
    def __init__(self):
        self.__class__.count += 1

print 'counter.count BEFORE ==',counter.count  # The result is 0
NewCounter1 = counter()
print '\nNewCounter1.__dict__ ==',NewCounter1.__dict__  # The result is {}
print 'NewCounter1.count    ==',NewCounter1.count # The result is 1
print 'counter.count AFTER  ==',counter.count  # The result is 1

NewCounter._dict_ is the namespace of the instance NewCounter1
print NewCounter1.count prints the same as print counter.count
However, 'count' (the string 'count') isn't in the namespace of NewCounter1, that is to say there is no attribute count in the namespace of the created instance !

How is it possible ?

That's because the instance is created without assignement to a 'count' identifier inside the _init_
-> there is no real creation of any attribute as a field in NewCounter1, that is to say no creation of INSTANCE attribute.

The consequence is that when the instruction
print 'NewCounter1.count ==',NewCounter1.count
is evaluated, the interpreter doesn't find an instance attribute in the NewCounter1 's namespace, and then goes to the class of the instance to search for the key 'count' in this class's namespace; there it finds 'count' as a key of a CLASS attribute and can take the VALUE of the object counter.count as a VALUE to display in response to the instruction.

A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched. When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes. http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

So, NewCounter1.count equals NewCounter1.__class__.count here means that the VALUE for NewCounter1.count, even if this one doesn't really exist, is the VALUE of the class attribute NewCounter1. class.count. Here "is" is the english verb, not the feature is of the language that tests the identities of two objects, and it means 'is considered to have '

When NewCounter2.__class__.count = 3 is executed, only the class attribute counter.count is affected. The namespaces of NewCounter1 and NewCounter2 remain empty and the same mechanism of going to the class to find the value of counter.count is followed.

.

At the end, when NewCounter2.count = 5 is executed , this time an INSTANCE attribute count is created as a field in the NewCounter2 object and 'count' appears in the namespace of NewCounter2 .
It doesn't overwrite anything, because there was nothing preceding in the instance's __dict__
No other change affects NewCounter1 and counter.count

The following code shows more explicitly the underlying events during execution:

from itertools import islice

class counter:
    count = 0
    def __init__(self):
        print ('  |  counter.count   first == %d  at  %d\n'
               '  |     self.count   first == %d  at  %d')\
               % (counter.count,id(counter.count),
                  self.count,id(self.count))

        self.__class__.count += 1 # <<=====

        print ('  |  counter.count  second == %d  at  %d\n'
               '  |     self.count  second == %d  at  %d\n'
               '  |  id(counter) == %d   id(self) == %d')\
               % (counter.count,id(counter.count),
                  self.count,id(self.count),
                  id(counter),id(self))



def display(*li):
    it = iter(li)
    for ch in it:
        nn = (len(ch)-len(ch.lstrip('\n')))*'\n'
        x = it.next()
        print '%s ==  %s %s' % (ch,x,'' if '__dict__' in ch else 'at '+str(id(x)))



display('counter.count AT START',counter.count)


print ('\n\n----- C1 = counter() ------------------------')
C1 = counter()
display('C1.__dict__',C1.__dict__,
        'C1.count ',C1.count,
        '\ncounter.count ',counter.count)


print ('\n\n----- C2 = counter() ------------------------')
C2 = counter()
print ('  -------------------------------------------') 
display('C1.__dict__',C1.__dict__,
        'C2.__dict__',C2.__dict__,
        'C1.count ',C1.count,
        'C2.count ',C2.count,
        'C1.__class__.count',C1.__class__.count,
        'C2.__class__.count',C2.__class__.count,
        '\ncounter.count ',counter.count)


print '\n\n------- C2.__class__.count = 3 ------------------------\n'
C2.__class__.count = 3
display('C1.__dict__',C1.__dict__,
        'C2.__dict__',C2.__dict__,
        'C1.count ',C1.count,
        'C2.count ',C2.count,
        'C1.__class__.count',C1.__class__.count,
        'C2.__class__.count',C2.__class__.count,
        '\ncounter.count ',counter.count)


print '\n\n------- C2.count = 5 ------------------------\n'
C2.count = 5
display('C1.__dict__',C1.__dict__,
        'C2.__dict__',C2.__dict__,
        'C1.count ',C1.count,
        'C2.count ',C2.count,
        'C1.__class__.count',C1.__class__.count,
        'C2.__class__.count',C2.__class__.count,
        '\ncounter.count ',counter.count)

result

counter.count AT START ==  0 at 10021628


----- C1 = counter() ------------------------
  |  counter.count   first == 0  at  10021628
  |     self.count   first == 0  at  10021628
  |  counter.count  second == 1  at  10021616
  |     self.count  second == 1  at  10021616
  |  id(counter) == 11211248   id(self) == 18735712
C1.__dict__ ==  {} 
C1.count  ==  1 at 10021616

counter.count  ==  1 at 10021616


----- C2 = counter() ------------------------
  |  counter.count   first == 1  at  10021616
  |     self.count   first == 1  at  10021616
  |  counter.count  second == 2  at  10021604
  |     self.count  second == 2  at  10021604
  |  id(counter) == 11211248   id(self) == 18736032
  -------------------------------------------
C1.__dict__ ==  {} 
C2.__dict__ ==  {} 
C1.count  ==  2 at 10021604
C2.count  ==  2 at 10021604
C1.__class__.count ==  2 at 10021604
C2.__class__.count ==  2 at 10021604

counter.count  ==  2 at 10021604


------- C2.__class__.count = 3 ------------------------

C1.__dict__ ==  {} 
C2.__dict__ ==  {} 
C1.count  ==  3 at 10021592
C2.count  ==  3 at 10021592
C1.__class__.count ==  3 at 10021592
C2.__class__.count ==  3 at 10021592

counter.count  ==  3 at 10021592


------- C2.count = 5 ------------------------

C1.__dict__ ==  {} 
C2.__dict__ ==  {'count': 5} 
C1.count  ==  3 at 10021592
C2.count  ==  5 at 10021568
C1.__class__.count ==  3 at 10021592
C2.__class__.count ==  3 at 10021592

counter.count  ==  3 at 10021592

.

An interesting thing to do is to add an instruction
self.count = counter.count
BEFORE the line
self.__class__.count += 1 # <<=====
to observe the changing of results

.

In conclusion, the point wasn't concerning __class__ but the mechanism of searching an attribute , and this mechanism is misleading when ignored.

like image 96
eyquem Avatar answered Sep 29 '22 08:09

eyquem


This line:

NewCounter2.__class__.count = 3

changes the static count of counter, but here:

NewCounter2.count = 5

NewCounter2 now has its own count attribute that hides the static count;
so that line has no effect on NewCounter1.
This is also why NewCounter2.__class__.count != NewCounter2.count.

like image 10
chown Avatar answered Sep 29 '22 08:09

chown


Rebinding (i.e. assigning) an attribute on an object with the same name as an attribute on the class shadows the attribute on the class. The object is always checked for attributes first, followed by the classes in MRO order.

like image 3
Ignacio Vazquez-Abrams Avatar answered Sep 29 '22 08:09

Ignacio Vazquez-Abrams