39

我想在 Python 中创建一个管理所有静态成员的类。这些成员应该在类的定义期间已经被初始化。由于稍后需要重新初始化静态成员,我会将这段代码放入一个类方法中。

我的问题:如何从课堂内部调用这个类方法?

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

任何帮助表示赞赏!

在此先感谢,沃尔克

4

7 回答 7

36

x=10您的示例中执行时,不仅该类不存在,而且该类方法也不存在。

Python中的执行从上到下。如果x=10在 classmethod 之上,则此时您无法访问 classmethod,因为它尚未定义。

即使你可以运行classmethod,也没关系,因为这个类还不存在,所以classmethod不能引用它。直到整个类块运行之后才会创建类,所以当你在类块内时,没有类。

如果您想排除一些类初始化,以便稍后以您描述的方式重新运行它,请使用类装饰器。类装饰器在类创建后运行,因此它可以很好地调用类方法。

>>> 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
于 2012-12-16T11:32:06.717 回答
5

您可以通过附加类名来调用类方法:

class.method

在您的代码中,这样的内容就足够了:

Test.static_init()

你也可以这样做:

static_init(Test)

要在您的类中调用它,请让您的代码执行以下操作:

Test.static_init()

我的工作代码:

class Test(object):

    @classmethod
    def static_method(cls):
        print("Hello")

    def another_method(self):
        Test.static_method()

Test().another_method()返回Hello

于 2012-12-16T10:51:31.603 回答
4

您不能classmethodclass定义中调用 a ,因为该类尚未完全定义,因此没有任何东西可以将该方法作为其第一个cls参数传递……这是一个典型的先有鸡还是先有蛋的问题。但是,您可以通过重载元类中的方法来解决此限制__new__(),并在创建类后从那里调用类方法,如下所示:

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

输出:

Hello
10
于 2012-12-16T12:11:34.027 回答
2

这次仔细重新阅读您的问题后,我可以想到两个解决方案。第一个是应用 Borg 设计模式。第二种是丢弃类方法并改用模块级函数。这似乎可以解决您的问题:

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

旧的,不正确的答案:

举个例子,希望对你有帮助:

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

无论如何,NlightNFotis 的答案是一个更好的答案:访问类方法时使用类名。它使您的代码不那么晦涩难懂。

于 2012-12-16T11:12:11.727 回答
0

这似乎是一个合理的解决方案:

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

该方法现在是密码的静态方法,没有引用类之外的任何内容。RotateCipher _RotateCipher如果您不希望人们自己使用它,您可以选择命名。

注意:我删除了Final, 因为我在 3.7 上运行它,但是在阅读了 Final 的文档后,我认为它不会影响解决方案?string还添加了一个缺少问题的导入。最后为抽象方法添加了一个实现,或者,也可以让 VigenereCipher 继承自abc.ABC

于 2019-12-25T01:48:08.267 回答
0

如果您的 classmethod 不经常使用,请进行惰性评估

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
   }

}
于 2021-11-14T18:28:22.230 回答
0

带有 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
于 2020-11-22T06:55:54.313 回答