How objects and class atributtes work

What’s a class attribute

What’s an instance attribute

The below ExampleClass is a basic python class with two attributes: class_attr and instance_attr.

class ExampleClass(object):
class_attr = 0
def __init__(self, instance_attr):
self.instance_attr = instance_attr

What are the differences between class and instance attributes

  • instance_attr is an instance attribute defined inside the constructor.
  • class_attr is a class attribute defined outside the constructor.

The instance_attr is only accesible from the scope of an object. The class attribute (class_attr) is accesible as both a property of the class and as a property of objects, as it is shared between all of them.

if __name__ == '__main__':foo = ExampleClass(1)
bar = ExampleClass(2)
# print the instance attribute of the object foo
print (foo.istance_attr)
#1
#print the instance attribute of the object var
print (bar.instance_attr)
#2
#print the class attribute of the class ExampleClass as a property of the class itself
print (ExampleClass.class_attr)
#0
#print the classattribute of the class as a proporty of the objects foo,bar
print (bar.class_attr)
#0
print (foo.class_attr)
#0
# try to print instance attribute as a class property
print (ExampleClass.instance_attr)
#AttributeError: type object 'ExampleClass' has no attribute 'instance_attr'

Notice that the class attribute can be accesed as a class property and as an instance property, however, accesing an instance attribute as a class property raises an AttributeError

So what is a namespace?

Python classes and objects have different namespaces, for our example, we have ExampleClass.__dict__ as a namespaces for our class and foo.__dict__(bar.__dict__) as a as a namespaces for our object foo(bar)

if __name__ = '__main__': foo = ExampleClass(1)
bar = ExampleClass(2)

print str(ExampleClass.__dict__)
#{'__module__': '__main__', 'class_attr': 0, '__dict__': <attribute '__dict__' of 'ExampleClass' objects>, '__weakref__': <attribute '__weakref__' of 'ExampleClass' objects>, '__doc__': None, '__init__': <function __init__ at 0x031192F0>}
print str(foo.__dict__)
#{'instance_attr': 1}
print str(bar.__dict__)
#{'instance_attr': 2}

When you acces an attribute (instance or class attribute) as a property of an object using the dot convention, searches first in the namespaces of that object for that attributte name.If it is found, it returns the value, otherwise, it seraches in the namespaces of the class. If nothing is found theres as well, it raises an AttributeError. The object namespaces is before the class namespaces.

If we find, in one class, both an instance attributte and a class attribute with the same name, the access to that name from your object will get you the value in the object namespaces.Below a simplified version of the lookup function.

def instlookup(inst, name):
## simplified algorithm...
if inst.__dict__.has_key(name):
return inst.__dict__[name]
else:
return inst.__class__.__dict__[name]

When you acces an attribute as a calss property, it searhes directly i the class namespaces for the name of that attribute. If it is found, it returns the value, otherwise, it raises an AttributeError.

Class Attributes Mutate to Be Instance Attributes

if __name__ = '__main__':
foo = ExampleClass(1)
bar = ExampleClass(2)
#print the class attribute as a porperty of a foo
print foo.class_attr
#0
#modify the class attribute as a foo property
foo.class_attr = 5
print foo.class_attr
#5
#print the class attribute as a porperty of a bar
print bar.class_attr
# 0
#oups !!!!

The class_attr is shared between all the objects of the class. HOmewever, ehen we changed the value from the foo object, this changed is not visible from the bar object which still has the old value, 0, rather than the new value , 5! Hold on … let’s check our namespaces and try to understand what the hell is happening.

if __name__ = '__main__':
foo = ExampleClass(1)
bar = ExampleClass(2)
print str(foo.__dict__)
#{'instance_attr': 1} the namespace of the foo object had only his instance attribute
print foo.class_attr
#0
foo.class_attr = 5
print str(foo.__dict__)
'''{'instance_attr': 1, 'class_attr': 5} once the affectation is done on the class attribute from the object foo , it is added as an instance attribute in his namespace'''

The affectation added a new instance attribute to the object foo and only to that object which is why in the previous example the object bar kept printing the class attribute.

With immutbale objects, this behavior is always the same.However, with utbale objects like lits, for example, it is not always the case, depending on how you modify your class attribute.

Let’s change our previous class to have a list as a class attribute.

class ExampleClass(object):
class_attr = []

def __init__(self, instance_attr):
self.instance_attr = instance_attr

We mmodify that list as a property of the object foo by appending a new element to it

if __name__ = '__main__':
foo = ExampleClass(1)
bar = ExampleClass(2)
#print the class attribute as a porperty of a foo
print foo.class_attr
# []
#modify the class attribute as a foo property
foo.class_attr.append(0)
print foo.class_attr
#[0]
#print the class attribute as a porperty of a bar
print bar.class_attr
# [0]

When a mutable class attribute is modified by an object, it does not mutate to turn into an instance attribute for that object. It stays. shared between all the objects of the class with the new elements appendes to it.

However, if you attach a new list to that attribute ( foo.class_attr = list("foo")) you will get the same behavior as the immutable objects.

if __name__ = '__main__':
foo = ExampleClass(1)
bar = ExampleClass(2)
#print the class attribute as a porperty of a foo
print foo.class_attr
# []
#modify the class attribute as a foo property
foo.class_attr = list("example")
print foo.class_attr
#[e,x,a,m,p,l,e]
#print the class attribute as a porperty of a bar
print bar.class_attr
# []

You can compare the namespace by yourself as proof of the previous behavior.

Explain __dict__attribute

A dictionary or other mapping object used to store an object’s (writable) attributes.

Remember, everything is an object in python. When I say everything, I mean everything like functions, classes, objects etc (Ya you read it right, classes. Classes are also objects). For example,

def func():
pass
func.temp = 1

print func.__dict__

class TempClass(object):
a = 1
def tempFunction(self):
pass

print TempClass.__dict__

output

{'temp': 1}
{'a': 1, '__module__': '__main__', 'tempFunction': <function tempFunction at 0x7f77951a95f0>, '__dict__': <attribute '__dict__' of 'TempClass' objects>, '__weakref__': <attribute '__weakref__' of 'TempClass' objects>, '__doc__': None}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store