Static class variables and methods in Python

Viewed 1542856

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

26 Answers

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.

@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

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]

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.

One special thing to note about static properties & instance properties, shown in the example below:

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

This means before assigning the value to instance property, if we try to access the property thru' instance, the static value is used. Each property declared in python class always has a static slot in memory.

Static methods in python are called classmethods. Take a look at the following code

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

Notice that when we call the method myInstanceMethod, we get an error. This is because it requires that method be called on an instance of this class. The method myStaticMethod is set as a classmethod using the decorator @classmethod.

Just for kicks and giggles, we could call myInstanceMethod on the class by passing in an instance of the class, like so:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

@dataclass definitions provide class-level names that are used to define the instance variables and the initialization method, __init__(). If you want class-level variable in @dataclass you should use typing.ClassVar type hint. The ClassVar type's parameters define the class-level variable's type.

from typing import ClassVar
from dataclasses import dataclass

@dataclass
class Test:
    i: ClassVar[int] = 10
    x: int
    y: int
    
    def __repr__(self):
        return f"Test({self.x=}, {self.y=}, {Test.i=})"

Usage examples:

> test1 = Test(5, 6)
> test2 = Test(10, 11)

> test1
Test(self.x=5, self.y=6, Test.i=10)
> test2
Test(self.x=10, self.y=11, Test.i=10)

One very interesting point about Python's attribute lookup is that it can be used to create "virtual variables":

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

Normally there aren't any assignments to these after they are created. Note that the lookup uses self because, although label is static in the sense of not being associated with a particular instance, the value still depends on the (class of the) instance.

Yes, definitely possible to write static variables and methods in python.

Static Variables : Variable declared at class level are called static variable which can be accessed directly using class name.

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

Instance variables: Variables that are related and accessed by instance of a class are instance variables.

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

Static Methods: Similar to variables, static methods can be accessed directly using class Name. No need to create an instance.

But keep in mind, a static method cannot call a non-static method in python.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

With Object datatypes it is possible. But with primitive types like bool, int, float or str bahaviour is different from other OOP languages. Because in inherited class static attribute does not exist. If attribute does not exists in inherited class, Python start to look for it in parent class. If found in parent class, its value will be returned. When you decide to change value in inherited class, static attribute will be created in runtime. In next time of reading inherited static attribute its value will be returned, bacause it is already defined. Objects (lists, dicts) works as a references so it is safe to use them as static attributes and inherit them. Object address is not changed when you change its attribute values.

Example with integer data type:

class A:
    static = 1


class B(A):
    pass


print(f"int {A.static}")  # get 1 correctly
print(f"int {B.static}")  # get 1 correctly

A.static = 5
print(f"int {A.static}")  # get 5 correctly
print(f"int {B.static}")  # get 5 correctly

B.static = 6
print(f"int {A.static}")  # expected 6, but get 5 incorrectly
print(f"int {B.static}")  # get 6 correctly

A.static = 7
print(f"int {A.static}")  # get 7 correctly
print(f"int {B.static}")  # get unchanged 6

Solution based on refdatatypes library:

from refdatatypes.refint import RefInt


class AAA:
    static = RefInt(1)


class BBB(AAA):
    pass


print(f"refint {AAA.static.value}")  # get 1 correctly
print(f"refint {BBB.static.value}")  # get 1 correctly

AAA.static.value = 5
print(f"refint {AAA.static.value}")  # get 5 correctly
print(f"refint {BBB.static.value}")  # get 5 correctly

BBB.static.value = 6
print(f"refint {AAA.static.value}")  # get 6 correctly
print(f"refint {BBB.static.value}")  # get 6 correctly

AAA.static.value = 7
print(f"refint {AAA.static.value}")  # get 7 correctly
print(f"refint {BBB.static.value}")  # get 7 correctly

Summarizing others' answers and adding, there are many ways to declare Static Methods or Variables in python.

1. Using staticmethod() as a decorator:

One can simply put a decorator above a method(function) declared to make it a static method. For eg.

class Calculator:
    @staticmethod
    def multiply(n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 * n2 * Res

print(Calculator.multiply(1, 2, 3, 4))              # 24

2. Using staticmethod() as a parameter function:

This method can receive an argument which is of function type, and it returns a static version of the function passed. For eg.

class Calculator:
    def add(n1, n2, *args):
        return n1 + n2 + sum(args)

Calculator.add = staticmethod(Calculator.add)
print(Calculator.add(1, 2, 3, 4))                   # 10

3. Using classmethod() as a decorator:

@classmethod has similar effect on a function as @staticmethod has, but this time, an additional argument is needed to be accepted in the function (similar to self parameter for instance variables). For eg.

class Calculator:
    num = 0
    def __init__(self, digits) -> None:
        Calculator.num = int(''.join(digits))

    @classmethod
    def get_digits(cls, num):
        digits = list(str(num))
        calc = cls(digits)
        return calc.num

print(Calculator.get_digits(314159))                # 314159

4. Using classmethod() as a parameter function:

@classmethod can also be used as a parameter function, in case one doesn't want to modify class definition. For eg.

class Calculator:
    def divide(cls, n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 / n2 / Res

Calculator.divide = classmethod(Calculator.divide)

print(Calculator.divide(15, 3, 5))                  # 1.0

5. Direct declaration

A method/variable declared outside all other methods, but inside a class is automatically static.

class Calculator:   
    def subtract(n1, n2, *args):
        return n1 - n2 - sum(args)

print(Calculator.subtract(10, 2, 3, 4))             # 1

The whole program

class Calculator:
    num = 0
    def __init__(self, digits) -> None:
        Calculator.num = int(''.join(digits))
    
    
    @staticmethod
    def multiply(n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 * n2 * Res


    def add(n1, n2, *args):
        return n1 + n2 + sum(args)
    

    @classmethod
    def get_digits(cls, num):
        digits = list(str(num))
        calc = cls(digits)
        return calc.num


    def divide(cls, n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 / n2 / Res


    def subtract(n1, n2, *args):
        return n1 - n2 - sum(args)
    



Calculator.add = staticmethod(Calculator.add)
Calculator.divide = classmethod(Calculator.divide)

print(Calculator.multiply(1, 2, 3, 4))              # 24
print(Calculator.add(1, 2, 3, 4))                   # 10
print(Calculator.get_digits(314159))                # 314159
print(Calculator.divide(15, 3, 5))                  # 1.0
print(Calculator.subtract(10, 2, 3, 4))             # 1

Refer to Python Documentation for mastering OOP in python.

To avoid any potential confusion, I would like to contrast static variables and immutable objects.

Some primitive object types like integers, floats, strings, and touples are immutable in Python. This means that the object that is referred to by a given name cannot change if it is of one of the aforementioned object types. The name can be reassigned to a different object, but the object itself may not be changed.

Making a variable static takes this a step further by disallowing the variable name to point to any object but that to which it currently points. (Note: this is a general software concept and not specific to Python; please see others' posts for information about implementing statics in Python).

So this is probably a hack, but I've been using eval(str) to obtain an static object, kind of a contradiction, in python 3.

There is an Records.py file that has nothing but class objects defined with static methods and constructors that save some arguments. Then from another .py file I import Records but i need to dynamically select each object and then instantiate it on demand according to the type of data being read in.

So where object_name = 'RecordOne' or the class name, I call cur_type = eval(object_name) and then to instantiate it you do cur_inst = cur_type(args) However before you instantiate you can call static methods from cur_type.getName() for example, kind of like abstract base class implementation or whatever the goal is. However in the backend, it's probably instantiated in python and is not truly static, because eval is returning an object....which must have been instantiated....that gives static like behavior.

You can use a list or a dictionary to get "static behavior" between instances.

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False

If you are attempting to share a static variable for, by example, increasing it across other instances, something like this script works fine:

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)

Put it this way the static variable is created when a user-defined a class come into existence and the define a static variable it should follow the keyword self,

class Student:

    the correct way of static declaration
    i = 10

    incorrect
    self.i = 10

Not like the @staticmethod but class variables are static method of class and are shared with all the instances.

Now you can access it like

instance = MyClass()
print(instance.i)

or

print(MyClass.i)

you have to assign the value to these variables

I was trying

class MyClass:
  i: str

and assigning the value in one method call, in that case it will not work and will throw an error

i is not attribute of MyClass

Class variable and allow for subclassing

Assuming you are not looking for a truly static variable but rather something pythonic that will do the same sort of job for consenting adults, then use a class variable. This will provide you with a variable which all instances can access (and update)

Beware: Many of the other answers which use a class variable will break subclassing. You should avoid referencing the class directly by name.

from contextlib import contextmanager

class Sheldon(object):
    foo = 73

    def __init__(self, n):
        self.n = n

    def times(self):
        cls = self.__class__
        return cls.foo * self.n
        #self.foo * self.n would give the same result here but is less readable
        # it will also create a local variable which will make it easier to break your code
    
    def updatefoo(self):
        cls = self.__class__
        cls.foo *= self.n
        #self.foo *= self.n will not work here
        # assignment will try to create a instance variable foo

    @classmethod
    @contextmanager
    def reset_after_test(cls):
        originalfoo = cls.foo
        yield
        cls.foo = originalfoo
        #if you don't do this then running a full test suite will fail
        #updates to foo in one test will be kept for later tests

will give you the same functionality as using Sheldon.foo to address the variable and will pass tests like these:

def test_times():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        assert s.times() == 146

def test_update():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        s.updatefoo()
        assert Sheldon.foo == 146

def test_two_instances():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        s3 = Sheldon(3)
        assert s.times() == 146
        assert s3.times() == 219
        s3.updatefoo()
        assert s.times() == 438

It will also allow someone else to simply:

class Douglas(Sheldon):
    foo = 42

which will also work:

def test_subclassing():
    with Sheldon.reset_after_test(), Douglas.reset_after_test():
        s = Sheldon(2)
        d = Douglas(2)
        assert d.times() == 84
        assert s.times() == 146
        d.updatefoo()
        assert d.times() == 168 #Douglas.Foo was updated
        assert s.times() == 146 #Seldon.Foo is still 73

def test_subclassing_reset():
    with Sheldon.reset_after_test(), Douglas.reset_after_test():
        s = Sheldon(2)
        d = Douglas(2)
        assert d.times() == 84 #Douglas.foo was reset after the last test
        assert s.times() == 146 #and so was Sheldon.foo

For great advice on things to watch out for when creating classes check out Raymond Hettinger's video https://www.youtube.com/watch?v=HTLu2DFOdTg

Related