Categories
class class-variables oop python static

Static class variables and methods in Python

2386

How do I create static class variables or methods in Python?

7

  • 16

    Yes. The absence of the keyword “static” might be misleading, but any object initialised inside the class (just one indent inside the class, and not in the constructor) is static. It is not dependent on instantiation (because it is not part of the constructor). As for methods, you can do that with an @staticmethod decorator.

    – user11991978

    Oct 30, 2020 at 9:16

  • 5

    using the term static for something that exists for all instancess of a class, always seemed odd to me

    Oct 31, 2020 at 20:10


  • 7

    @TonySuffolk66 Blame (I think) C++, which simply appropriated the existing keyword “static” from C (where it indicated that the lifetime of the variable persisted beyond the scope in which it was declared). C++ extended that to mean a variable whose value was outside the “scope” of a single instance of a class. Python (more logically) simply calls them class attributes, as they are attributes associated with the class itself, rather than an instance of the class.

    – chepner

    Dec 23, 2020 at 13:25

  • 3

    @chepner static actually means several things in C++ (abbreviated definitions due to very strict comment length). There’s file scoped static inherited from C which means “this variable/function is usable in this file only”, there’s class scoped static which means “this method or field is associated with the type and not any instance of the type” (rarely used in C++ but common in C#/Java/ObjC, for example, I think this is what the OP is asking about), there’s local variable static in functions which means “this variable’s value is retained between function calls”.

    – jrh

    Apr 18, 2021 at 23:41


  • 4

    Flipping the switch into “opinion”, I think a lot of the times, static methods in C#/Java were made because the languages took a hard line “no functions” stance, in C#/Java you can only have methods (i.e., a function that’s part of a class), Python doesn’t have this restriction (which is for the best, in my opinion). I’d rather use C++’s namespaces or import functions from a file (Python), personally, than create a class for no reason other than to hold functions. OOP has its uses, but sometimes you just want a function.

    – jrh

    Apr 18, 2021 at 23:47


2233

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.

13

  • 23

    I’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.

    – Seb

    Oct 2, 2012 at 15:58


  • 67

    @theJollySin the pythonic way for constants is to not grow a class for constants. Just have some const.py with PI = 3.14 and you can import it everywhere. from const import PI

    – Giszmo

    Jun 21, 2013 at 17:54

  • 56

    This 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 attribute i it does not behave like a static variable in other languages. See millerdev’s answer, Yann’s answer, and my answer below.

    Dec 19, 2014 at 15:40


  • 9

    so only one copy of i(static variable) will be in memory even if I create hundreds of instance of this class ?

    Jul 21, 2017 at 12:53

  • 3

    For anyone’s interested who’s Daniel mentioned in @Dubslow comment, it is millerdev (wayback machine)

    – OfirD

    Feb 26, 2018 at 21:22


727

@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

Pictorial Representation Of Above Example

8

  • 9

    I 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)

    – Mark

    Aug 20, 2014 at 13:48


  • 10

    A 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 do x = Test(), x.i = 12, assert x.i == Test.i.

    Dec 19, 2014 at 15:05


  • 3

    So I could say all variables are static initially and then accessing instances makes instance variables at runtime?

    – Ali

    Nov 4, 2015 at 7:17

  • 4

    Perhaps this is interesting: if you define a method in Test that changes Test.i, that will affect BOTH Test.i and t.i values.

    – Pablo

    Mar 7, 2018 at 13:41

  • 2

    @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?

    Sep 21, 2018 at 10:12

259

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 my_method(self):
        pass

    # class method:
    @classmethod
    def my_class_method(cls):
        pass

    # static method:
    @staticmethod
    def my_static_method():
        pass

As usual, the first argument to my_method() is bound to the class instance object. In contrast, the first argument to my_class_method() is bound to the class object itself (e.g., in this case, Test). For my_static_method(), 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

15

  • 2

    I 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

    Mar 29, 2015 at 15:22

  • 2

    @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 doing Test.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 🙂

    May 11, 2015 at 15:20

  • 3

    @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 🙂

    May 14, 2015 at 21:54

  • 3

    @OleThomsenBuus: Check my answer for a simpler metaclass that does the job.

    Feb 22, 2017 at 16:40

  • 3

    @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.

    Dec 8, 2017 at 16:14