Are static class variables possible in Python?
Solution 1
Variables declared inside the class definition, but not inside a method are class or static variables:
>>> class MyClass:
... i = 3
...
>>> MyClass.i
3
As @millerdev points out, this creates a class-level i
variable, but this is distinct from any instance-level i
variable, so you could have
>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)
This is different from C++ and Java, but not so different from C#, where a static member can't be accessed using a reference to an instance.
See what the Python tutorial has to say on the subject of classes and class objects.
@Steve Johnson has already answered regarding static methods, also documented under "Built-in Functions" in the Python Library Reference.
class C:
@staticmethod
def f(arg1, arg2, ...): ...
@beidy recommends classmethods over staticmethod, as the method then receives the class type as the first argument.
Solution 2
@Blair Conrad said static variables declared inside the class definition, but not inside a method are class or "static" variables:
>>> class Test(object):
... i = 3
...
>>> Test.i
3
There are a few gotcha's here. Carrying on from the example above:
>>> t = Test()
>>> t.i # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i # we have not changed the "static" variable
3
>>> t.i # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6 # changes to t do not affect new instances of Test
# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}
Notice how the instance variable t.i
got out of sync with the "static" class variable when the attribute i
was set directly on t
. This is because i
was re-bound within the t
namespace, which is distinct from the Test
namespace. If you want to change the value of a "static" variable, you must change it within the scope (or object) where it was originally defined. I put "static" in quotes because Python does not really have static variables in the sense that C++ and Java do.
Although it doesn't say anything specific about static variables or methods, the Python tutorial has some relevant information on classes and class objects.
@Steve Johnson also answered regarding static methods, also documented under "Built-in Functions" in the Python Library Reference.
class Test(object):
@staticmethod
def f(arg1, arg2, ...):
...
@beid also mentioned classmethod, which is similar to staticmethod. A classmethod's first argument is the class object. Example:
class Test(object):
i = 3 # class (or static) variable
@classmethod
def g(cls, arg):
# here we can use 'cls' instead of the class name (Test)
if arg > cls.i:
cls.i = arg # would be the same as Test.i = arg1
Solution 3
Static and Class Methods
As the other answers have noted, static and class methods are easily accomplished using the built-in decorators:
class Test(object):
# regular instance method:
def MyMethod(self):
pass
# class method:
@classmethod
def MyClassMethod(cls):
pass
# static method:
@staticmethod
def MyStaticMethod():
pass
As usual, the first argument to MyMethod()
is bound to the class instance object. In contrast, the first argument to MyClassMethod()
is bound to the class object itself (e.g., in this case, Test
). For MyStaticMethod()
, none of the arguments are bound, and having arguments at all is optional.
"Static Variables"
However, implementing "static variables" (well, mutable static variables, anyway, if that's not a contradiction in terms...) is not as straight forward. As millerdev pointed out in his answer, the problem is that Python's class attributes are not truly "static variables". Consider:
class Test(object):
i = 3 # This is a class attribute
x = Test()
x.i = 12 # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i # ERROR
assert Test.i == 3 # Test.i was not affected
assert x.i == 12 # x.i is a different object than Test.i
This is because the line x.i = 12
has added a new instance attribute i
to x
instead of changing the value of the Test
class i
attribute.
Partial expected static variable behavior, i.e., syncing of the attribute between multiple instances (but not with the class itself; see "gotcha" below), can be achieved by turning the class attribute into a property:
class Test(object):
_i = 3
@property
def i(self):
return type(self)._i
@i.setter
def i(self,val):
type(self)._i = val
## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##
class Test(object):
_i = 3
def get_i(self):
return type(self)._i
def set_i(self,val):
type(self)._i = val
i = property(get_i, set_i)
Now you can do:
x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i # no error
assert x2.i == 50 # the property is synced
The static variable will now remain in sync between all class instances.
(NOTE: That is, unless a class instance decides to define its own version of _i
! But if someone decides to do THAT, they deserve what they get, don't they???)
Note that technically speaking, i
is still not a 'static variable' at all; it is a property
, which is a special type of descriptor. However, the property
behavior is now equivalent to a (mutable) static variable synced across all class instances.
Immutable "Static Variables"
For immutable static variable behavior, simply omit the property
setter:
class Test(object):
_i = 3
@property
def i(self):
return type(self)._i
## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##
class Test(object):
_i = 3
def get_i(self):
return type(self)._i
i = property(get_i)
Now attempting to set the instance i
attribute will return an AttributeError
:
x = Test()
assert x.i == 3 # success
x.i = 12 # ERROR
One Gotcha to be Aware of
Note that the above methods only work with instances of your class - they will not work when using the class itself. So for example:
x = Test()
assert x.i == Test.i # ERROR
# x.i and Test.i are two different objects:
type(Test.i) # class 'property'
type(x.i) # class 'int'
The line assert Test.i == x.i
produces an error, because the i
attribute of Test
and x
are two different objects.
Many people will find this surprising. However, it should not be. If we go back and inspect our Test
class definition (the second version), we take note of this line:
i = property(get_i)
Clearly, the member i
of Test
must be a property
object, which is the type of object returned from the property
function.
If you find the above confusing, you are most likely still thinking about it from the perspective of other languages (e.g. Java or c++). You should go study the property
object, about the order in which Python attributes are returned, the descriptor protocol, and the method resolution order (MRO).
I present a solution to the above 'gotcha' below; however I would suggest - strenuously - that you do not try to do something like the following until - at minimum - you thoroughly understand why assert Test.i = x.i
causes an error.
REAL, ACTUAL Static Variables - Test.i == x.i
I present the (Python 3) solution below for informational purposes only. I am not endorsing it as a "good solution". I have my doubts as to whether emulating the static variable behavior of other languages in Python is ever actually necessary. However, regardless as to whether it is actually useful, the below should help further understanding of how Python works.
UPDATE: this attempt is really pretty awful; if you insist on doing something like this (hint: please don't; Python is a very elegant language and shoe-horning it into behaving like another language is just not necessary), use the code in Ethan Furman's answer instead.
Emulating static variable behavior of other languages using a metaclass
A metaclass is the class of a class. The default metaclass for all classes in Python (i.e., the "new style" classes post Python 2.3 I believe) is type
. For example:
type(int) # class 'type'
type(str) # class 'type'
class Test(): pass
type(Test) # class 'type'
However, you can define your own metaclass like this:
class MyMeta(type): pass
And apply it to your own class like this (Python 3 only):
class MyClass(metaclass = MyMeta):
pass
type(MyClass) # class MyMeta
Below is a metaclass I have created which attempts to emulate "static variable" behavior of other languages. It basically works by replacing the default getter, setter, and deleter with versions which check to see if the attribute being requested is a "static variable".
A catalog of the "static variables" is stored in the StaticVarMeta.statics
attribute. All attribute requests are initially attempted to be resolved using a substitute resolution order. I have dubbed this the "static resolution order", or "SRO". This is done by looking for the requested attribute in the set of "static variables" for a given class (or its parent classes). If the attribute does not appear in the "SRO", the class will fall back on the default attribute get/set/delete behavior (i.e., "MRO").
from functools import wraps
class StaticVarsMeta(type):
'''A metaclass for creating classes that emulate the "static variable" behavior
of other languages. I do not advise actually using this for anything!!!
Behavior is intended to be similar to classes that use __slots__. However, "normal"
attributes and __statics___ can coexist (unlike with __slots__).
Example usage:
class MyBaseClass(metaclass = StaticVarsMeta):
__statics__ = {'a','b','c'}
i = 0 # regular attribute
a = 1 # static var defined (optional)
class MyParentClass(MyBaseClass):
__statics__ = {'d','e','f'}
j = 2 # regular attribute
d, e, f = 3, 4, 5 # Static vars
a, b, c = 6, 7, 8 # Static vars (inherited from MyBaseClass, defined/re-defined here)
class MyChildClass(MyParentClass):
__statics__ = {'a','b','c'}
j = 2 # regular attribute (redefines j from MyParentClass)
d, e, f = 9, 10, 11 # Static vars (inherited from MyParentClass, redefined here)
a, b, c = 12, 13, 14 # Static vars (overriding previous definition in MyParentClass here)'''
statics = {}
def __new__(mcls, name, bases, namespace):
# Get the class object
cls = super().__new__(mcls, name, bases, namespace)
# Establish the "statics resolution order"
cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))
# Replace class getter, setter, and deleter for instance attributes
cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
# Store the list of static variables for the class object
# This list is permanent and cannot be changed, similar to __slots__
try:
mcls.statics[cls] = getattr(cls,'__statics__')
except AttributeError:
mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
# Check and make sure the statics var names are strings
if any(not isinstance(static,str) for static in mcls.statics[cls]):
typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
# Move any previously existing, not overridden statics to the static var parent class(es)
if len(cls.__sro__) > 1:
for attr,value in namespace.items():
if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
for c in cls.__sro__[1:]:
if attr in StaticVarsMeta.statics[c]:
setattr(c,attr,value)
delattr(cls,attr)
return cls
def __inst_getattribute__(self, orig_getattribute):
'''Replaces the class __getattribute__'''
@wraps(orig_getattribute)
def wrapper(self, attr):
if StaticVarsMeta.is_static(type(self),attr):
return StaticVarsMeta.__getstatic__(type(self),attr)
else:
return orig_getattribute(self, attr)
return wrapper
def __inst_setattr__(self, orig_setattribute):
'''Replaces the class __setattr__'''
@wraps(orig_setattribute)
def wrapper(self, attr, value):
if StaticVarsMeta.is_static(type(self),attr):
StaticVarsMeta.__setstatic__(type(self),attr, value)
else:
orig_setattribute(self, attr, value)
return wrapper
def __inst_delattr__(self, orig_delattribute):
'''Replaces the class __delattr__'''
@wraps(orig_delattribute)
def wrapper(self, attr):
if StaticVarsMeta.is_static(type(self),attr):
StaticVarsMeta.__delstatic__(type(self),attr)
else:
orig_delattribute(self, attr)
return wrapper
def __getstatic__(cls,attr):
'''Static variable getter'''
for c in cls.__sro__:
if attr in StaticVarsMeta.statics[c]:
try:
return getattr(c,attr)
except AttributeError:
pass
raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
def __setstatic__(cls,attr,value):
'''Static variable setter'''
for c in cls.__sro__:
if attr in StaticVarsMeta.statics[c]:
setattr(c,attr,value)
break
def __delstatic__(cls,attr):
'''Static variable deleter'''
for c in cls.__sro__:
if attr in StaticVarsMeta.statics[c]:
try:
delattr(c,attr)
break
except AttributeError:
pass
raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
def __delattr__(cls,attr):
'''Prevent __sro__ attribute from deletion'''
if attr == '__sro__':
raise AttributeError('readonly attribute')
super().__delattr__(attr)
def is_static(cls,attr):
'''Returns True if an attribute is a static variable of any class in the __sro__'''
if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
return True
return False
Solution 4
You can also add class variables to classes on the fly
>>> class X:
... pass
...
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1
And class instances can change class variables
class X:
l = []
def __init__(self):
self.l.append(1)
print X().l
print X().l
>python test.py
[1]
[1, 1]
Solution 5
Personally I would use a classmethod whenever I needed a static method. Mainly because I get the class as an argument.
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
or use a decorator
class myObj(object):
@classmethod
def myMethod(cls)
For static properties.. Its time you look up some python definition.. variable can always change. There are two types of them mutable and immutable.. Also, there are class attributes and instance attributes.. Nothing really like static attributes in the sense of java & c++
Why use static method in pythonic sense, if it has no relation whatever to the class! If I were you, I'd either use classmethod or define the method independent from the class.
Andrew Walker
Engineer specialising in software engineering for robotics and mechatronics Engineer. Completed a PhD in 2011 on "Hard Real-Time Motion Planning for Autonomous Vehicles". Currently employed in the Defence & Aerospace industry. I enjoy working in Python, Mathematica and C/C++, and I'm passionate about developing and verifying high integrity software.
Updated on July 10, 2022Comments
-
Andrew Walker almost 2 years
Is it possible to have static class variables or methods in Python? What syntax is required to do this?
-
tmsppc over 11 yearsI'm just learning Python, but the advantages of
@classmethod
over@staticmethod
AFAIK is that you always get the name of the class the method was invoked on, even if it's a subclass. A static method lacks this information, so it cannot call an overridden method, for example. -
zakdances about 11 yearsWill the new class variables stick even if the class is imported into another module?
-
john_science about 11 years@Blair Is this something I want to do. As an example, if you wanted to be able to access
PI = 3.14
lots of times in a class (ignoring math libraries for argument sake), would your really doMyClass.PI
in all those places? What is the Pythonic approach? Doesn't this look kind of cluttered and ugly? Or am I wrong? -
Giszmo almost 11 years@theJollySin the pythonic way for constants is to not grow a class for constants. Just have some
const.py
withPI = 3.14
and you can import it everywhere.from const import PI
-
Mark over 9 yearsI suggest you extend the example just a little: if, after setting Test.i=6, you then instantiate a new object (e.g., u=Test()), the new object will "inherit" the new class value (e.g., u.i==6)
-
yantrab over 9 yearsWhy is it even a class if you aren't going to instantiate it? This feels like twisting Python to turn it into Java....
-
Rick over 9 yearsA way to keep the static variables in sync is to make them properties:
class Test(object):
,_i = 3
,@property
,def i(self)
,return type(self)._i
,@i.setter
,def i(self,val):
,type(self)._i = val
. Now you can dox = Test()
,x.i = 12
,assert x.i == Test.i
. -
Rick over 9 yearsYou could also just use
@property
, which is the same as using a descriptor, but it's a lot less code. -
Rick over 9 yearsThis answer is likely to confuse the static variable issue. To begin with,
i = 3
is not a static variable, it is a class attribute, and since it is distinct from an instance-level attributei
it does not behave like a static variable in other languages. See millerdev's answer, Yann's answer, and my answer below. -
Rick over 9 yearsThe Borg idiom is a better way to handle this.
-
Muhammed Refaat about 9 yearsI tried to use your way but I faced a problem, kindly have a look at my question here stackoverflow.com/questions/29329850/get-static-variable-value
-
Ole Thomsen Buus about 9 years@RickTeachey: I guess you should generally view anything you do on the class Instance
Test
(before using it for instantiating instances) as being in the domain of meta-programming? For instance, you alter the class-behavior by doingTest.i = 0
(here you simply destroy the property object entirely). I guess the "property-mechanism" kicks in only on property-access on instances of a class (unless you change underlying behavior using a meta-class as an intermediate, perhaps). Btw, please finish this answer :-) -
Rick about 9 years@OleThomsenBuus Um, I wouldn't quite put it that way. It would be more correct to call it "class object programming" and "instance object programming" - because classes ARE objects. The meta programming is only necessary (in this particular case) if you wish to erase the line between instance and class, which is where a "static variable" lives. The
property
behavior works because instance objects delegate attribute access to their class in certain cases (eg when the attribute doesn't exist). Also: I promise I will get around to finishing up what I had in mind for this answer. Just been busy. -
Ole Thomsen Buus about 9 years@RickTeachey Thanks :-) Your metaclass in the end is interesting but is actually a bit too complex for my liking. It might be useful in a large framework/application where this mechanism is absolutely required. Anyway, this exemplifies that if new (complex) non-default meta-behavior is really needed, Python makes it possible :)
-
yoyo almost 9 yearsSeems this sort of class variable cannot be accessed during
__del__
? In my limited experience the class variables are going away before__del__
runs. Is this expected? -
Ali over 8 yearsSo I could say all variables are static initially and then accessing instances makes instance variables at runtime?
-
Błażej Michalik over 7 years
@classmethod
superiority over@staticmethod
is IMO the fact, that you can easily refactorize class methods and break them up into smaller ones, while with@staticmethod
you are basically stuck with what you've got. Refactorizing static methodC.m()
would require adding class referenceC
to each call, not to mention fetching class fields. This probably has negative side effects on polymorphism in some cases. -
gauteh over 7 yearsThis would give a quite different behavior if you were using a mutable variable like a list:
i = []
, thenm.i.append (1)
would change the contents of the variable in the class as well. -
Ethan Furman about 7 years@OleThomsenBuus: Check my answer for a simpler metaclass that does the job.
-
Saurabh Jain almost 7 yearsso only one copy of
i
(static variable) will be in memory even if I create hundreds of instance of this class ? -
Rick almost 7 yearsyes, but in this case
hasattr(SomeClass, 'x')
isFalse
. i doubt this is what anyone means by a static variable at all. -
jmunsch almost 7 years@RickTeachey lol, saw your static variable code, stackoverflow.com/a/27568860/2026508 +1 internet sir, and i thought hasattr didn't work like that? so is
some_var
immutable, and statically defined, or is it not? What does outside getter access have to do with a variable being static or not? i have so many questions now. would love to hear some answers when you get the time. -
Rick almost 7 yearsYeah that metaclass is pretty ridiculous. I'm not certain I understand the questions but to my mind,
some_var
above isn't a class member at all. In Python all class members can be accessed from outside the class. -
Rick almost 7 yearsThe
nonlocal
keywoard "bumps" the scope of the variable. The scope of a class body definition is independent of the scope it finds itself in- when you saynonlocal some_var
, that is just creating a non-local (read: NOT in the class definition scope) name reference to another named object. Therefore it doesn't get attached to the class definition because it is not in the class body scope. -
Davis Herring over 6 yearsVariables are not mutable or immutable; objects are. (However, an object can, with varying degrees of success, try to prevent assignment to certain of its attributes.)
-
taper over 6 yearsI tried and found that
@property
is not synced between class instances in python 3. Is that a feature of old python language? -
Rick over 6 years@taper as explained in the answer above,
@property
is in sync between class instances (for all versions of python going back a long time). the class member of the same name as the property attached to the class object itself is not in sync with the instances. -
taper over 6 yearsDear @RickTeachey , I cannot understand your point because of some terminologies I am not familiar with. But what I mean could be demonstrated by this gist. If you run it in python 3.6.3 (which I did), it gives different result as claimed by this answer.
-
Rick over 6 years@taper You are correct; I have edited the answer to fix the problem (can't believe it's been sitting there wrong for so long!). Sorry for the confusion.
-
OfirD about 6 yearsFor anyone's interested who's Daniel mentioned in @Dubslow comment, it is millerdev (wayback machine)
-
Pablo about 6 yearsPerhaps this is interesting: if you define a method in Test that changes Test.i, that will affect BOTH Test.i and t.i values.
-
cowbert about 6 years@sdream yes, since the only reference to
i
is its assignment on the class. In order to access this from an instance is viainstance.__class__.i
-
Pedro almost 6 yearsYes. Classes are effectively singletons, regardless of the namespace you call them from.
-
NightOwl19 over 5 years@millerdev, like u mentioned Python doesn't have static variables as C++ or JAVA have..So will it be okay to say, Test.i is more of a class variable rather than a static variable?
-
stevepastelan over 5 years@NedBatchelder It's an abstract class, intended only for subclassing (and instantiating the subclasses)
-
yantrab over 5 yearsI hope the subclasses don't use super() to invoke the
__new__
of its parents... -
Howard Lovatt about 5 yearsReally like the write up. One nit pick the immutable version isn't really immutable because you can assign a lambda to get_i that returns a different value.
-
Rick about 5 years@HowardLovatt it doesn't actually work that way. if you try to monkeypatch
get_i
after the property has been created, it will not overwrite the property. try it. -
Howard Lovatt about 5 yearsThe following changes
t.i
for me:t.get_i = lambda s: 7 ; assert t.i == 7 # ERROR i changed
. -
Rick about 5 years@HowardLovatt your assert statement should be
assert t.i == 3
and there is no error. -
Howard Lovatt about 5 years@RickTeachey No it passes the assert for me, i.e.
t.i
is 7 - it has changed. This is true on Pythonista which uses 3.6 (I think) and on 3.7 (via PyCharm). -
Rick about 5 years@HowardLovatt that's odd. It will definitely will not change the value on any version of python I've ever used if you set the property first and then swap out the getter (or setter) without overwriting the property. That's just not how it works.
-
ShadowRanger almost 5 yearsUmm... static methods are made with
@staticmethod
;@classmethod
is (obviously) for class methods (which are primarily intended for use as alternate constructors, but can serve in a pinch as static methods which happen to receive a reference to the class that they were called through). -
Thomas Weller almost 5 yearsThe indentation is broken. This won't execute
-
Angel O'Sphere over 4 yearsJava and C++ use static (ill use of the word, imho) exactly as you use instance versus class attribute. A class attribute/method is static in Java and C++, no difference, except that in Python the first parameter to a class method call is the class.
-
Amr ALHOSSARY over 4 years@Gregory you said "And class instances can change class variables" Actually this example is called access not modification. The modification was done by the object itself through its own append() function.
-
chepner over 3 yearsI vaguely remember reading some comments from Guido van Rossum regarding the introduction of class and static methods. IIRC, static methods are what correspond more closely to class methods in other languages. The Python
classmethod
was more or less a "mistake", though it became the natural way to define an alternate class constructor to use in place of__new__
. -
Andrew over 3 yearsWhat you call 'static' variables are, I think, class variables. Viz: class A(): inner_var = 0 class B(A): pass A.inner_var = 15 B.inner_var = 30 print ("A:static=" + str(A.inner_var)) print ("B:static=" + str(B.inner_var)) # Output: # A:static=15 # B:static=30
-
Alex over 3 yearsThis is a little bit scary when coming from other programming languages and expect it to be just a private/protected field.
-
Sunil Garg almost 3 yearshey hi can you please help me on this stackoverflow.com/questions/68301705/…
-
joseville over 2 yearsFor completion, it should be noted that "static" variables that are modified in-place will stay in sync. E.g. if
i
had been alist
instead of anint
, then whether you dot.i[0] = 5
orTest.i[0] = 5
, everyTest
instance and theTest
class itself will see the change: @millerdev's example withi
as alist
instead of anint
. I think this is because you're not overwriting the value ofi
, a reference, but you're following the reference and modifying the underlying data structure in-place. -
ingyhere over 2 years@RicksupportsMonica Using
type(self)
doesn't work as provided, at least in Python 3.9. I read your full answer, but my tests don't bear it out. Incidentally, I am declaring a number ofstaticmethod
functions in the class.