Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Static objects in a class

I was wondering how to properly keep a static object in a python class. In this case I want to have a static dictionary

Just a simple example of what I'm looking for:

class dTest:
    # item I want to be static
    d = {"First":1}

>>> a = dTest()
>>> a.d
{'First': 1}
>>> dTest.d["Second"] = 2
>>> a.d["Third"] = 3
>>> a.d
{'Second': 2, 'Third': 3, 'First': 1}
>>> dTest.d
{'Second': 2, 'Third': 3, 'First': 1}

Now if I directly call the class and replace d with a new dictionary

>>> dTest.d = {}
>>> a.d
{}

However I'd also like to have the same functionality if I replace a.d with a new dictionary

>>> a.d = {"Fourth":4}
>>> a.d
{'Fourth': 4}
>>> dTest.d
{}

My desired result right now would be for dTest.d to be the same to a.d (dTest.d being {'Fourth': 4}).

Is there a proper practice for this, or do I have to just make sure I only ever edit the object if I do it from an instance of dTest?

Thanks!

like image 690
flakes Avatar asked Oct 12 '25 06:10

flakes


2 Answers

There are two different types of variables when dealing with classes: instance variables and class variables.

Class variables are accessible from all instances of a class, as well as the class itself:

>>> class Foo(object):
...    class_var = {'one': 1}
...
>>> Foo.class_var
{'one': 1}
>>> a, b = Foo(), Foo()
>>> a.class_var
{'one': 1}
>>> b.class_var
{'one': 1}
>>> a.class_var is b.class_var 
True # These are the same object
>>> 

Instance variables are only accessible from the specific instance that they are assigned to:

>>> class Foo(object):
...     def __init__(self):
...             self.instance_var1 = {'two': 2}
... 
>>> foo = Foo()
>>> foo.instance_var2 = {'three': 3}
>>> foo.instance_var1
{'two': 2}
>>> foo.instance_var2
{'three': 3}
>>> Foo.instance_var1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute 'instance_var1'

Instance variables can override class variables:

>>> class Foo(object):
...     my_var = {'one': 1} # This a class variable
... 
>>> foo = Foo()
>>> foo.my_var = {'two': 2} # Override class my_var with instance my_var
>>> foo.my_var
{'two': 2}
>>> Foo.my_var
{'one': 1}
>>> foo.__class__.my_var # But you can still access the class variable like this.
{'one': 1}
>>> 

To answer your question specifically. If you would like to alter the class variable, you must set it from the class, not the instance, so:

dTest.d = {'blah': 'blah'}

or

a.__class__.d = {'blah': 'blah'}

If you just do

a.d = {'blah': 'blah'}

You create an instance variable of a that shadows, or overrides the class variable with the same name.

like image 71
Joel Cornett Avatar answered Oct 14 '25 18:10

Joel Cornett


The dictionary you are using within your class is shared, when you add key-value pairs to d, either with a.d[key]=... or dTest.d[key]=..., you don't change the reference of d, so it's still a shared class attribute.

But if you try to assign something to a.d, like a.d=123 or a.d=[11,22], you create an attribute d for instance a, and a.d no longer refers to dType.d. See Assignment statements:

Note: If the object is a class instance and the attribute reference occurs on both sides of the assignment operator, the RHS expression, a.x can access either an instance attribute or (if no instance attribute exists) a class attribute. The LHS target a.x is always set as an instance attribute, creating it if necessary. Thus, the two occurrences of a.x do not necessarily refer to the same attribute: if the RHS expression refers to a class attribute, the LHS creates a new instance attribute as the target of the assignment:

class Cls:
    x = 3             # class variable
inst = Cls()
inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
like image 43
zhangxaochen Avatar answered Oct 14 '25 18:10

zhangxaochen