I'm learning Python, but have no OOP experience. I'm entering the following lines in IDLE (Python 3.3), and the behavior of the objects is confusing me:
>>> class IBAN:
ISOcode = ' '
checkDigits = '00'
class CCC:
bankCode = '0000'
branchCode = '0000'
checkBank = '0'
checkAccount = '0'
account = '0000000000'
>>> >>> numberOne = IBAN()
>>> numberOne.CCC
<class '__main__.IBAN.CCC'>
>>> numberOne.CCC.bankCode
'0000'
>>> numberOne.ISOcode = 'ES'
>>> IBAN.ISOcode
' '
>>> numberOne.ISOcode
'ES'
>>> IBAN.ISOcode = 'FR'
>>> numberOne.ISOcode
'ES'
>>> IBAN.ISOcode = 'FR'
>>> IBAN.ISOcode
'FR'
>>> numberTwo = IBAN()
>>> numberTwo.ISOcode
'FR'
>>> IBAN.ISOcode = 'IT'
>>> IBAN.ISOcode
'IT'
>>> numberOne.ISOcode
'ES'
>>> numberTwo.ISOcode
'IT'
>>> IBAN.ISOcode is numberTwo.ISOcode
True
>>>
Why is the ISOcode attribute of object numberTwo
the same as the ISOcode of IBAN
, but not the same as the ISOcode of objectnumberOne
?
I expected something like this:
>>> numberOne.ISOcode
'ES'
>>> numberTwo.ISOcode
'FR'
This, however, doesn't match my results.
I found a similar question on StackOverflow, that made me think the results should be:
>>> numberOne.ISOcode
'IT'
>>> numberTwo.ISOcode
'IT'
But that doesn't match my results either.
Can anybody explain or link to resources that explain the behavior of Classes in Python? I tried to study the Python Tutorial and "Unifying types and classes in Python 2.2" by Guido van Rossum, but I wasn't able to understand what they were saying.
Thank you!
The class is a different object than instances of the class. Classes have attributes, and instances have attributes. Every time you try to access an attribute of an instance, if the instance does not have the attribute, it is looked up on the class. If you set an attribute on an instance, it is always set on the instance, even if that means it shadows an attribute of the same name on the class.
In your class IBAN, you defined an attribute ISOcode
on the class. When you do numberOne.ISOcode = 'ES'
, you set a new attribute on the instance. You have decoupled the value of numberOne.ISOcode
from the value of IBAN.ISOcode
. But any new instances you create (like numberTwo
) will still not have their own instance ISOcode. When you then set IBAN.ISOcode
, you are changing the class attribute.
You can think of the class attribute as like a "default". When you do obj.attr
, if obj
does not have its own attribute attr
, it takes the value that the class has at that moment. If you change the value of class's attribute, you change the default; if you later try to access the attribute on an instance that doesn't have its own attribute, they will see the new default that you created.
Here is a somewhat more streamlined example:
>>> class Foo(object):
... attr = "Something"
>>> one = Foo()
>>> two = Foo()
At first, all have the same value, because I didn't specify any instance-specific values, so one
and two
both get their value from the class.
>>> Foo.attr
'Something'
>>> one.attr
'Something'
>>> two.attr
'Something'
Now I will set a new value just for one
. This does not affect Foo
, and two
is still gettings its value from Foo
, so two
is also not affected.
>>> one.attr = "This is only for #1"
>>> Foo.attr
'Something'
>>> one.attr
'This is only for #1'
>>> two.attr
'Something'
Now I will set a new value on the class Foo
. This doesn't affect one
, because one
has its own value. But since two
doesn't have its own value, it is getting its value from the class, and since I changed the class's value, this changes what value I get from two
:
>>> Foo.attr = "This is the class value"
>>> Foo.attr
'This is the class value'
>>> one.attr
'This is only for #1'
>>> two.attr
'This is the class value'
The thing to remember is that every attribute lookup is always dynamically computed right when you do it. Creating an instance does not automatically copy class attributes from the class to the instance. When you access an attribute on an instance, the instances says to itself "Do I have my own attribute with this name? If so, return that value. If not, return the class's value." It asks itself this every time you try to access an attribute; there is no moment at which the instance becomes "independent" of the class.