Je voudrais créer une classe dans Python qui gère avant tout les membres statiques. Ces membres doivent déjà être initiés lors de la définition de la classe. En raison du fait qu'il sera nécessaire de réinitialiser le membres statiques plus tard, je mettrais ce code dans une méthode de classe.
Ma question: comment puis-je appeler cette méthode de classe depuis l'intérieur de la classe?
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
Toute aide est appréciée!
Merci d'avance, Volker
Au moment où x=10
est exécuté dans votre exemple, non seulement la classe n'existe pas, mais la méthode de classe n'existe pas non plus.
Exécution en Python va de haut en bas. Si x=10
est au-dessus de la méthode de classe, il n'y a aucun moyen d'accéder à la méthode de classe à ce stade, car elle n'a pas encore été définie.
Même si vous pouviez exécuter la méthode de classe, cela n'aurait pas d'importance, car la classe n'existe pas encore, donc la méthode de classe ne pouvait pas s'y référer. La classe n'est créée qu'après l'exécution de l'ensemble du bloc de classe, donc tant que vous êtes à l'intérieur du bloc de classe, il n'y a pas de classe.
Si vous souhaitez factoriser une certaine initialisation de classe afin de pouvoir la réexécuter plus tard de la manière que vous décrivez, utilisez un décorateur de classe. Le décorateur de classe s'exécute après la création de la classe, il peut donc appeler très bien la méthode de classe.
>>> 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
Vous appelez une méthode de classe en ajoutant le nom de classe de la même manière:
class.method
Dans votre code, quelque chose comme ça devrait suffire:
Test.static_init()
Vous pouvez également faire ceci:
static_init(Test)
Pour l'appeler dans votre classe, demandez à votre code de faire ceci:
Test.static_init()
Mon code de travail:
class Test(object):
@classmethod
def static_method(cls):
print("Hello")
def another_method(self):
Test.static_method()
et Test().another_method()
renvoie Hello
Vous ne pouvez pas appeler un classmethod
dans la définition class
car la classe n'a pas encore été entièrement définie, il n'y a donc rien pour passer la méthode comme premier argument cls
. ..un problème classique de poulet et d'oeufs. Cependant, vous pouvez contourner cette limitation en surchargeant la méthode__new__()
dans une métaclasse et en appelant la méthode de classe à partir de là après la création de la classe, comme illustré ci-dessous:
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
Production:
Hello
10
Après avoir relu votre question attentivement cette fois, je peux penser à deux solutions. La première consiste à appliquer le motif de conception Borg. La seconde consiste à ignorer la méthode de classe et à utiliser une fonction de niveau module à la place. Cela semble résoudre votre problème:
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
Voici un exemple, j'espère que cela aide:
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
Quoi qu'il en soit, la réponse de NlightNFotis est meilleure: utilisez le nom de classe lors de l'accès aux méthodes de classe. Cela rend votre code moins obscur.
Cela semble être une solution raisonnable:
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()
La méthode est maintenant une méthode statique du chiffrement, rien en dehors des classes n'est référencé. Vous pouvez choisir de nommer RotateCipher
_RotateCipher
à la place, si vous ne voulez pas que les gens l'utilisent seul.
Remarque: j'ai supprimé le Final
, car je l'ai exécuté sur 3.7, mais après avoir lu la documentation sur Final, je ne pense pas que cela affecterait la solution? Ajout également d'une importation pour string
dont la question était manquante. Et enfin ajouté une implémentation pour les méthodes abstraites, alternativement, aurait pu laisser VigenereCipher hériter de abc.ABC
ainsi que.