I would like to create a class in Python that manages above all static members. These members should be initiliazed during definition of the class already. Due to the fact that there will be the requirement to reinitialize the static members later on I would put this code into a classmethod.
My question: How can I call this classmethod from inside the class?
class Test[]:
# static member
x = None
# HERE I WOULD LOVE TO CALL SOMEHOW static_init!
# initialize static member in classmethod, so that it can be
#reinitialized later on again
@classmethod
def static_init[cls]:
cls.x = 10
Any help is appreciated!
Thanks in advance, Volker
asked Dec 16, 2012 at 10:47
2
At the time that x=10
is executed in your example, not only does the class not exist, but the classmethod doesn't exist either.
Execution in
Python goes top to bottom. If x=10
is above the classmethod, there is no way you can access the classmethod at that point, because it hasn't been defined yet.
Even if you could run the classmethod, it wouldn't matter, because the class doesn't exist yet, so the classmethod couldn't refer to it. The class is not created until after the entire class block runs, so while you're inside the class block, there's no class.
If you want to factor out some class initialization so you can re-run it later in the way you describe, use a class decorator. The class decorator runs after the class is created, so it can call the classmethod just fine.
>>> def deco[cls]:
... cls.initStuff[]
... return cls
>>> @deco
... class Foo[object]:
... x = 10
...
... @classmethod
... def initStuff[cls]:
... cls.x = 88
>>> Foo.x
88
>>> Foo.x = 10
>>> Foo.x
10
>>> Foo.initStuff[] # reinitialize
>>> Foo.x
88
answered Dec 16, 2012 at 11:32
BrenBarnBrenBarn
232k35 gold badges396 silver badges375 bronze badges
4
You call a class method by appending the class name likewise:
class.method
In your code something like this should suffice:
Test.static_init[]
You could also do this:
static_init[Test]
To call it inside your class, have your code do this:
Test.static_init[]
My working code:
class Test[object]:
@classmethod
def static_method[cls]:
print["Hello"]
def another_method[self]:
Test.static_method[]
and Test[].another_method[]
returns Hello
answered Dec 16, 2012 at 10:51
NlightNFotisNlightNFotis
9,3835 gold badges41 silver badges65 bronze badges
4
You can't call a classmethod
in the class
definition because the class hasn't been fully defined yet, so there's nothing to pass the method as its first cls
argument...a classic chicken-and-egg problem. However you can work around this limitation by overloading the __new__[]
method in a metaclass, and calling the classmethod from there after the class
has been created as illustrated below:
class Test[object]:
# nested metaclass definition
class __metaclass__[type]:
def __new__[mcl, classname, bases, classdict]:
cls = type.__new__[mcl, classname, bases, classdict] # creates class
cls.static_init[] # call the classmethod
return cls
x = None
@classmethod
def static_init[cls]: # called by metaclass when class is defined
print["Hello"]
cls.x = 10
print Test.x
Output:
Hello
10
answered Dec 16, 2012 at 12:11
martineaumartineau
115k25 gold badges160 silver badges283 bronze badges
After re-reading your question carefully this time I can think of two solutions. The first one is to apply the Borg design pattern. The second one is to discard the class method and use a module level function instead. This appears to solve your problem:
def _test_static_init[value]:
return value, value * 2
class Test:
x, y = _test_static_init[20]
if __name__ == "__main__":
print Test.x, Test.y
Old, incorrect answer:
Here's an example, I hope it helps:
class Test:
x = None
@classmethod
def set_x_class[cls, value]:
Test.x = value
def set_x_self[self]:
self.__class__.set_x_class[10]
if __name__ == "__main__":
obj = Test[]
print Test.x
obj.set_x_self[]
print Test.x
obj.__class__.set_x_class[15]
print Test.x
Anyway, NlightNFotis's answer is a better one: use the class name when accessing the class methods. It makes your code less obscure.
answered Dec 16, 2012 at 11:12
ZalewaPLZalewaPL
1,0941 gold badge6 silver badges14 bronze badges
4
This seems like a reasonable solution:
from __future__ import annotations
from typing import ClassVar, Dict
import abc
import string
class Cipher[abc.ABC]:
@abc.abstractmethod
def encrypt[self, plaintext: str] -> str:
pass
@abc.abstractmethod
def decrypt[self, ciphertext: str] -> str:
pass
class RotateCipher[Cipher, abc.ABC]:
@staticmethod
def rotate[n: int] -> str:
return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]
class VigenereCipher[RotateCipher]:
_TABLE: ClassVar[Dict[str, str]] = dict[{[chr[i + ord["A"]], RotateCipher.rotate[i]] for i in range[26]}]
def encrypt[self, plaintext: str] -> str:
pass
def decrypt[self, plaintext: str] -> str:
pass
vc = VigenereCipher[]
The method is now a static method of the cipher, nothing outside the classes is referenced. You could opt to name RotateCipher
_RotateCipher
instead, if you don't want people using it by itself.
Note: I removed the Final
, as I ran this on 3.7, but after reading the documentation
on Final, I don't think it would affect the solution? Also added an import for string
which the question was missing. And finally added an implementation for the abstract methods, alternatively, could have let VigenereCipher inherit from abc.ABC
as well.
answered Dec 25, 2019 at 1:48
GrismarGrismar
23.5k4 gold badges29 silver badges49 bronze badges
person class with add[] @classmethod
class Person:
def __init__[self,a,b]:
self.first = a
self.second = b
@classmethod
def add[cls,a,b]:
return a+b
def __repr__[self]:
return ['{}'.format[Person.add[self.first,self.second]]]
p = Person[10,20]
print[p]
o/p : 30
answered Nov 22, 2020 at 6:55
If your classmethod is not used very often do a lazy evaluation
class A[] {
# this does not work: x=A.initMe[]
@classmethod
def initMe[cls] {
if not hasattr[cls,"x"]:
# your code her
cls.x=# your result
pass
@classmethod
def f1[cls] {
# needs initMe
cls.initMe[]
# more code using cls.x
}
}
answered Nov 14, 2021 at 18:28
Wolfgang FahlWolfgang Fahl
13.9k9 gold badges86 silver badges167 bronze badges