Class (static) variables and methods
Asked Answered
G

27

2568

How do I create class (i.e. static) variables or methods in Python?

Gambrill answered 16/9, 2008 at 1:46 Comment(8)
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.Brazilein
using the term static for something that exists for all instancess of a class, always seemed odd to meCrosswind
@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.Aeneous
@Aeneous 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".Havener
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.Havener
@Brazilein How does it go with the @dataclass then? It seems to define its members in the class, just like the "static" / "class data" members used to be defined for regular classes, however it treats them as "instance" members instead :q Inconsistent much? :qKegan
You can add a class variable outside a method: class Search: COUNT = 100 Any instance will see this value, and change it anywhere as Search.COUNT = 250. The change will be visible in all instances.Reimpression
staticmethod function take your function as parameter and returns its static version. You can use it as decoratorHartwell
A
2353

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.

Absa answered 16/9, 2008 at 1:51 Comment(12)
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.Faires
@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 do MyClass.PI in all those places? What is the Pythonic approach? Doesn't this look kind of cluttered and ugly? Or am I wrong?Schlimazel
@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 PINicolnicola
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.Indus
Seems 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?Mendelsohn
@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 method C.m() would require adding class reference C to each call, not to mention fetching class fields. This probably has negative side effects on polymorphism in some cases.Fondafondant
This would give a quite different behavior if you were using a mutable variable like a list: i = [], then m.i.append (1) would change the contents of the variable in the class as well.Erastianism
so only one copy of i(static variable) will be in memory even if I create hundreds of instance of this class ?Amu
For anyone's interested who's Daniel mentioned in @Dubslow comment, it is millerdev (wayback machine)Terrel
@sdream yes, since the only reference to i is its assignment on the class. In order to access this from an instance is via instance.__class__.iMahmoud
I 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__.Aeneous
This is a little bit scary when coming from other programming languages and expect it to be just a private/protected field.Churchwell
C
769

@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

Commemoration answered 16/9, 2008 at 3:4 Comment(8)
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.Indus
So I could say all variables are static initially and then accessing instances makes instance variables at runtime?Agonizing
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.Dovetailed
@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?Bathe
For completion, it should be noted that "static" variables that are modified in-place will stay in sync. E.g. if i had been a list instead of an int, then whether you do t.i[0] = 5 or Test.i[0] = 5, every Test instance and the Test class itself will see the change: @millerdev's example with i as a list instead of an int. I think this is because you're not overwriting the value of i, a reference, but you're following the reference and modifying the underlying data structure in-place.Cavanaugh
@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 of staticmethod functions in the class.Synthiasyntonic
I think this answer lacks specifying some of the gotchas where inheritance comes into playClothespin
I would like to see one more Test object a = Test() be created before modifying the value of i and show that the value does change if you don't create an object instance of i. making it "static" in the sense of accessing the same variable. Other than that this is my favorite answer @millerdev.Pinard
I
289

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
Indus answered 19/12, 2014 at 15:16 Comment(15)
I tried to use your way but I faced a problem, kindly have a look at my question here https://mcmap.net/q/17850/-get-static-variable-valueGamekeeper
@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 :-)Suilmann
@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.Indus
@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 :)Suilmann
@OleThomsenBuus: Check my answer for a simpler metaclass that does the job.Tarrance
I tried and found that @property is not synced between class instances in python 3. Is that a feature of old python language?Exudation
@Exudation 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.Indus
Dear @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.Exudation
@Exudation 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.Indus
Really 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.Tripody
@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.Indus
The following changes t.i for me: t.get_i = lambda s: 7 ; assert t.i == 7 # ERROR i changed.Tripody
@HowardLovatt your assert statement should be assert t.i == 3 and there is no error.Indus
@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).Tripody
@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.Indus
N
47

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]
Necrotomy answered 17/9, 2008 at 8:6 Comment(3)
Will the new class variables stick even if the class is imported into another module?Hinterland
Yes. Classes are effectively singletons, regardless of the namespace you call them from.Forestall
@Necrotomy 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.Mayday
P
29

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.

Penguin answered 16/9, 2008 at 2:2 Comment(2)
Variables 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.)Corsetti
Java 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.Irisation
S
25

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.

Sogdiana answered 8/3, 2012 at 6:6 Comment(0)
G
24

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
Gem answered 16/9, 2008 at 2:5 Comment(1)
Umm... 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).Methionine
T
18

It is possible to have static class variables, but probably not worth the effort.

Here's a proof-of-concept written in Python 3 -- if any of the exact details are wrong the code can be tweaked to match just about whatever you mean by a static variable:


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

and in use:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

and some tests:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
Tarrance answered 25/3, 2016 at 9:2 Comment(0)
Z
17

When define some member variable outside any member method, the variable can be either static or non-static depending on how the variable is expressed.

  • CLASSNAME.var is static variable
  • INSTANCENAME.var is not static variable.
  • self.var inside class is not static variable.
  • var inside the class member function is not defined.

For example:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

The results are

self.var is 2
A.var is 1
self.var is 2
A.var is 3
Zeta answered 26/3, 2013 at 17:56 Comment(1)
The indentation is broken. This won't executeReconsider
S
16

@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)
Sequence answered 27/1, 2021 at 12:0 Comment(0)
E
13

You could also enforce a class to be static using metaclass.

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

Then whenever by accident you try to initialize MyClass you'll get an StaticClassError.

Enochenol answered 20/11, 2011 at 12:6 Comment(4)
Why is it even a class if you aren't going to instantiate it? This feels like twisting Python to turn it into Java....Goddard
The Borg idiom is a better way to handle this.Indus
@NedBatchelder It's an abstract class, intended only for subclassing (and instantiating the subclasses)Catfall
I hope the subclasses don't use super() to invoke the __new__ of its parents...Goddard
C
12

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.

Corsetti answered 21/9, 2017 at 4:4 Comment(0)
S
12

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
Selfness answered 6/8, 2021 at 13:34 Comment(0)
S
11

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!!
Selinski answered 14/12, 2018 at 8:1 Comment(1)
What 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=30Briscoe
F
10

In regards to this answer, for a constant static variable, you can use a descriptor. Here's an example:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

resulting in ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

You can always raise an exception if quietly ignoring setting value (pass above) is not your thing. If you're looking for a C++, Java style static class variable:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

Have a look at this answer and the official docs HOWTO for more information about descriptors.

Farming answered 3/7, 2014 at 12:14 Comment(1)
You could also just use @property, which is the same as using a descriptor, but it's a lot less code.Indus
E
10

Absolutely Yes, Python by itself don't have any static data member explicitly, but We can have by doing so

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

output

0
0
1
1

explanation

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
Emerick answered 22/2, 2017 at 13:1 Comment(0)
A
7

The best way I found is to use another class. You can create an object and then use it on other objects.

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

With the example above, I made a class named staticFlag.

This class should present the static var __success (Private Static Var).

tryIt class represented the regular class we need to use.

Now I made an object for one flag (staticFlag). This flag will be sent as reference to all the regular objects.

All these objects are being added to the list tryArr.


This Script Results:

False
False
False
False
False
True
True
True
True
True
Alika answered 27/2, 2013 at 17:0 Comment(0)
A
7

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.

Androgyne answered 26/10, 2021 at 12:34 Comment(1)
A bit scary calculator... int(''.join(digits)) will raise an error because digits are passed as list of integers and join doesn't do the cast in automatically. Notice get_digits is also a setter... the Calculator program although if almost syntactically consistent it is a collection of bad programming structuresEisler
I
6

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

Invercargill answered 17/9, 2008 at 4:1 Comment(0)
A
5

Static Variables in Class factory python3.6

For anyone using a class factory with python3.6 and up use the nonlocal keyword to add it to the scope / context of the class being created like so:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world
Assuan answered 1/1, 2017 at 2:56 Comment(4)
yes, but in this case hasattr(SomeClass, 'x') is False. i doubt this is what anyone means by a static variable at all.Indus
@RickTeachey lol, saw your static variable code, https://mcmap.net/q/17578/-class-static-variables-and-methods +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.Assuan
Yeah 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.Indus
The 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 say nonlocal 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.Indus
C
4

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.

Celestyna answered 7/4, 2020 at 12:34 Comment(0)
P
4

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)
Pruinose answered 14/5, 2020 at 19:31 Comment(0)
B
3

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
Brandtr answered 3/11, 2019 at 18:27 Comment(0)
J
2

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
Jigger answered 17/7, 2020 at 19:36 Comment(0)
D
2

You can create the class variable x, the instance variable name, the instance method test1(self), the class method test2(cls) and the static method test3() as shown below:

class Person:
    x = "Hello" # Class variable

    def __init__(self, name):
        self.name = name # Instance variable
    
    def test1(self): # Instance method
        print("Test1")

    @classmethod
    def test2(cls): # Class method
        print("Test2")
        
    @staticmethod
    def test3(): # Static method
        print("Test3")

I explain about class variable in my answer and class method and static method in my answer and instance method in my answer.

Dropping answered 19/11, 2022 at 15:49 Comment(0)
N
1

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
Novitiate answered 18/2, 2021 at 7:58 Comment(0)
M
1

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

Morrismorrison answered 6/5, 2022 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.