DEV Community

Cover image for Python: class attributes, some behaviours we should be aware of.
Be Hai Nguyen
Be Hai Nguyen

Posted on

3

Python: class attributes, some behaviours we should be aware of.

We look at some behaviours of class attributes which can help to make life easier for us -- mere programmers.

We will look at the following three (3) behaviours:

  1. From the Python official documentation:

    9.4. Random Remarks

    If the same attribute name occurs in both an instance and in a class, then attribute lookup prioritizes the instance.

    https://docs.python.org/3/tutorial/classes.html:
  2. Setting the value of a class attribute via the class will propagate the new value to class instances, whom have not overridden the value of this class attribute. This is in conformance with the documentation above.
  3. Setting the value of a class attribute via the class will propagate the new value down to child classes, but no vice versa.

Let's explore these points via examples.

❶ Attribute lookup prioritises the instance.

This is an example from the documentation page quoted above, I have tweaked it a tiny bit.

class Warehouse:
    purpose = 'Storage'
    region = 'west'
Enter fullscreen mode Exit fullscreen mode

Then:

w1 = Warehouse()
print("1: ", w1.purpose, w1.region)
Enter fullscreen mode Exit fullscreen mode

Output -- these are default class attributes' values:

1:  Storage west
Enter fullscreen mode Exit fullscreen mode
w2 = Warehouse()
w2.region = 'east'
print("2: ", w2.purpose, w2.region)
Enter fullscreen mode Exit fullscreen mode

We just instantiate an instance of Warehouse, then override the value of the region attribute with 'east' (*):

2:  Storage east
Enter fullscreen mode Exit fullscreen mode

-- (*): please note, what I've just written above might not be correct... According to the quoted documentation, the statement w2.region = 'east' might actually mean assigning the new attribute region to instance w2, rather than override as I've written.

❷ Setting the value via class propagates the new value to instances whom have not provided their own value.

We continue with examples in ❶:

Warehouse.region = 'north'
w3 = Warehouse()
print("3: ", w3.purpose, w3.region)
Enter fullscreen mode Exit fullscreen mode

Instance w3 is created with whatever the class attributes' values of Warehouse class:

3:  Storage north
Enter fullscreen mode Exit fullscreen mode

Setting Warehouse.region = 'north', how does this affect the existing two (2) instances w1 and w2?

print(f"4: w1.region: {w1.region}, w2.region: {w2.region}")
Enter fullscreen mode Exit fullscreen mode
4: w1.region: north, w2.region: east
Enter fullscreen mode Exit fullscreen mode

w1 has not set its own value for the region attribute, setting the new value via the class Warehouse does propagate back to instance w1. w2, on the hand, has set its own, so it was not affected.

❸ Setting the value propagates from the parent class to child classes, but not vice versa.

Consider the following classes:

class Engine:
    started = False;

class TwoStrokeEngine(Engine):
    pass

class FourStrokeEngine(Engine):
    pass
Enter fullscreen mode Exit fullscreen mode

In their initial state, started is False for all classes:

print(f"1. Engine.started: {Engine.started}")
print(f"1. TwoStrokeEngine.started: {TwoStrokeEngine.started}")
print(f"1. FourStrokeEngine.started: {FourStrokeEngine.started}\n")
Enter fullscreen mode Exit fullscreen mode
1. Engine.started: False
1. TwoStrokeEngine.started: False
1. FourStrokeEngine.started: False
Enter fullscreen mode Exit fullscreen mode

Let's set Engine.started to True:

Engine.started = True

print(f"2. Engine.started: {Engine.started}")
print(f"2. TwoStrokeEngine.started: {TwoStrokeEngine.started}")
print(f"2. FourStrokeEngine.started: {FourStrokeEngine.started}\n")
Enter fullscreen mode Exit fullscreen mode
2. Engine.started: True
2. TwoStrokeEngine.started: True
2. FourStrokeEngine.started: True
Enter fullscreen mode Exit fullscreen mode

Let's switch Engine.started back to False:

Engine.started = False

print(f"3. Engine.started: {Engine.started}")
print(f"3. TwoStrokeEngine.started: {TwoStrokeEngine.started}")
print(f"3. FourStrokeEngine.started: {FourStrokeEngine.started}\n")
Enter fullscreen mode Exit fullscreen mode
3. Engine.started: False
3. TwoStrokeEngine.started: False
3. FourStrokeEngine.started: False
Enter fullscreen mode Exit fullscreen mode

Let's set FourStrokeEngine.started to True:

FourStrokeEngine.started = True

print(f"4. Engine.started: {Engine.started}")
print(f"4. TwoStrokeEngine.started: {TwoStrokeEngine.started}")
print(f"4. FourStrokeEngine.started: {FourStrokeEngine.started}\n")
Enter fullscreen mode Exit fullscreen mode
4. Engine.started: False
4. TwoStrokeEngine.started: False
4. FourStrokeEngine.started: True
Enter fullscreen mode Exit fullscreen mode

-- We can see that, setting the value propagates from the parent class to child classes, but not vice versa.

What about their instances? Continue on with the examples above:

"""
FourStrokeEngine.started is True from above.
"""

engine = Engine()
two_stroke_engine = TwoStrokeEngine()
four_stroke_engine = FourStrokeEngine()
four_stroke_engine1 = FourStrokeEngine()

print(f"5. engine.started: {engine.started}")
print(f"5. two_stroke_engine.started: {two_stroke_engine.started}")
print(f"5. four_stroke_engine.started: {four_stroke_engine.started}")
print(f"5. four_stroke_engine1.started: {four_stroke_engine1.started}\n")

Engine.started = True

print(f"6. engine.started: {engine.started}")
print(f"6. two_stroke_engine.started: {two_stroke_engine.started}")
print(f"6. four_stroke_engine.started: {four_stroke_engine.started}")
print(f"6. four_stroke_engine1.started: {four_stroke_engine1.started}\n")
Enter fullscreen mode Exit fullscreen mode

Output:

5. engine.started: False
5. two_stroke_engine.started: False
5. four_stroke_engine.started: True
5. four_stroke_engine1.started: True

6. engine.started: True
6. two_stroke_engine.started: True
6. four_stroke_engine.started: True
6. four_stroke_engine1.started: True
Enter fullscreen mode Exit fullscreen mode

Let's set TwoStrokeEngine.started to False, and see what happens to existing instances:

TwoStrokeEngine.started = False

print(f"7. engine.started: {engine.started}")
print(f"7. two_stroke_engine.started: {two_stroke_engine.started}")
print(f"7. four_stroke_engine.started: {four_stroke_engine.started}")
print(f"7. four_stroke_engine1.started: {four_stroke_engine1.started}\n")
Enter fullscreen mode Exit fullscreen mode
7. engine.started: True
7. two_stroke_engine.started: False
7. four_stroke_engine.started: True
7. four_stroke_engine1.started: True
Enter fullscreen mode Exit fullscreen mode

It makes sense that only two_stroke_engine.started was affected.

I did get caught out on some of these issues... And hence this post. I do hope you find this post useful. Thank you for reading and stay safe as always.

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay