How objects and class atributtes work

Carlos Alvarez
5 min readOct 8, 2019

What’s a class attribute

A class attribute is a python variable that belongs to a class rather than a particular object. It is shared between all the objects of this class and it is defined outside the constructor function, __init__(self,...), of the class.

What’s an instance attribute

An instance attribute is a python variable belonging to one, and only one, object. This variable is only accesible in the scope of this object and it is defined inside the constructor function, __init__(self,..) of the class.

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?

In python, a namespaces is a mapping between objects and names. TO keep it simple, let’s say it is a python dictionary that has as a key to the name of the objec and its value as a value.DIfferent namespaces can coexist with the property while the names within them are independiente.

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

Yeah, it might seem weird, but they do! Let’s consider the following scenario and then comment it together.

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

Basically it contains all the attributes which describe the object under question. It can be used to alter or read the attributes

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

Python class attribute may be useful in different cases, however, they must be used with caution in order to avoid unexpected behaviors.

--

--