Andrés Felipe García Rendón
6 min readJan 14, 2020

--

Class and instance attributes

How object and class attributes work

What is an attribute?

An attribute is a variable that is looked up on another object using dot syntax: obj.attribute. The way Python is designed, attribute lookups can do a variety of things, and that variety can sometimes lead to bugs if you don't really understand what is happening (this is what the documentation you linked to warns about).

The most basic issue is that an attribute lookup can find either a value stored in the object’s instance dictionary, or it can find something from the object’s class (or a base class, if there’s inheritance going on). Methods are functions stored in the class, but you usually use them by looking them up on an instance (which “binds” the method, inserting the object as the first arguemnt when the method is called).

The exact sequence of what is checked when is a bit complicated (I described the full process in an answer to another question), but at the most basic level, instance attributes usually take precedence over class attribute.

If an instance attribute and a class attribute with the same name both exist, usually only the instance attribute will be accessible. This can be very confusing if it is unintended.

Consider the following code:

class Foo(object):
def __init__(self, lst):
self.lst = lst
def sum(self):
self.sum = sum(self.lst)
return self.sum
f = Foo([1,2,3])print(f.sum())
print(f.sum())

At the bottom of this code, we make two identical calls. The first works just fine, but the second will raise an exception.

This is because the first time we look up f.sum we find a method in the Foo class. We can call the method with no problems. The trouble comes from the fact that the sum method assigns the result of its calculation (the sum of the elements in self.lst) to an instance attribute also named sum. This hides the sum method from view.

When second f.sum() call looks up f.sum, it finds the instance attribute, containing the integer 6, rather than the expected method. An integer is not callable, so we get an exception.

The solution, of course, is not to use the same name for the method and attribute. The code above is a pretty trivial example. The bugs caused by this sort of thing in more complex code can be much more difficult to figure out.

If you’re writing code that adds attributes to objects you don’t know much about, you should be careful to avoid common names. If you’re writing a mixin class, consider using two leading underscores in the attribute names to trigger Python’s name mangling, which is designed for exactly this sort of situation.

Python Classes and Methods

Python is an “object-oriented programming language.” This means that almost all the code is implemented using a special construct called classes. Programmers use classes to keep related things together. This is done using the keyword “class,” which is a grouping of object-oriented constructs.

By the end of this tutorial you will be able to:

  1. Define what is a class
  2. Describe how to create a class
  3. Define what is a method
  4. Describe how to do object instantiation
  5. Describe how to create instance attributes in Python

What is a class?

A class is a code template for creating objects. Objects have member variables and have behaviour associated with them. In python a class is created by the keyword class.

An object is created using the constructor of the class. This object will then be called the instance of the class. In Python we create instances in the following manner

Instance = class(arguments)

How to create a class

The simplest class can be created using the class keyword. For example, let’s create a simple, empty class with no functionalities.

>>> class Snake:
... pass
...
>>> snake = Snake()
>>> print(snake)
<__main__.Snake object at 0x7f315c573550>

Attributes and Methods in class:

A class by itself is of no use unless there is some functionality associated with it. Functionalities are defined by setting attributes, which act as containers for data and functions related to those attributes. Those functions are called methods.

Attributes:

You can define the following class with the name Snake. This class will have an attribute name.

>>> class Snake:
... name = "python" # set an attribute `name` of the class
...

You can assign the class to a variable. This is called object instantiation. You will then be able to access the attributes that are present inside the class using the dot . operator. For example, in the Snake example, you can access the attribute name of the class Snake.

>>> # instantiate the class Snake and assign it to variable snake
>>> snake = Snake()
>>> # access the class attribute name inside the class Snake.
>>> print(snake.name)
python

Methods

Once there are attributes that “belong” to the class, you can define functions that will access the class attribute. These functions are called methods. When you define methods, you will need to always provide the first argument to the method with a self keyword.

For example, you can define a class Snake, which has one attribute name and one method change_name. The method change name will take in an argument new_name along with the keyword self.

>>> class Snake:
... name = "python"
...
... def change_name(self, new_name): # note that the first argument is self
... self.name = new_name # access the class attribute with the self keyword
...

Now, you can instantiate this class Snake with a variable snake and then change the name with the method change_name.

>>> # instantiate the class
>>> snake = Snake()
>>> # print the current object name
>>> print(snake.name)
python
>>> # change the name using the change_name method
>>> snake.change_name("anaconda")
>>> print(snake.name)
anaconda

Instance attributes in python and the init method

You can also provide the values for the attributes at runtime. This is done by defining the attributes inside the init method. The following example illustrates this.

class Snake:    def __init__(self, name):
self.name = name
def change_name(self, new_name):
self.name = new_name

Now you can directly define separate attribute values for separate objects. For example,

>>> # two variables are instantiated
>>> python = Snake("python")
>>> anaconda = Snake("anaconda")
>>> # print the names of the two variables
>>> print(python.name)
python
>>> print(anaconda.name)
anaconda

How does Python deal with the object and class attributes using the __dict__?

When you define a Python class the execution engine creates a __dict__ to store all of its methods and any class attributes you define as parts of it.

Normal class instantiation in Python entails creating an object with another “dunder dict” (__dict__) attribute. This then links to all of the instance’s other attributes (as initialized by the class’ __init__ method, and/or by its __new__ code.

The attribute access mechanics of the class entail searching the instance dictionary, then its class dictionary, and then it traverses upward through the entire class hierarchy until it finds a match, or runs out of dictionaries to search … in which case the engine would raise an AttributeError Exception.

So, basically the entire (normal) class hierarchy in Python is built around dictionaries.

You can modify an instance, or even a class, and future access to that instance will encounter the modifications first in the search path. This is called “monkey patching” and it’s sometimes useful, though usually frowned on.

Also there are special methods __getattr__, __getattribute__, __setattr__ and __delattr__ which over-ride the default attribute/method handling … so your classes can behave as if instances dynamically have or lack attributes in just about any way you like. (One top of that you can define “properties” attributes controlled by custom “getter” and “setter” … and deleter … methods).

There is an alternative way to implement Python classes using the __slots__ attribute. This will result in classes with NO __dict__ such that instances will only support the attributes listed in __slots__ (and the special attributes which Python needs to support objects in general). Instances of such classes don’t support “monkey patching” but can be instantiated with far less memory overhead. So __slots__ are normally used for performance reasons on collections of large numbers of objects.

resources:

--

--

Andrés Felipe García Rendón

Web Developer graduated from Holberton School. With experience in as C, Python, NodeJS, React, React Native, HTML, CSS, Boostrap and Flexbox