如何从 Python 中的类内部访问类方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13900515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 09:53:28  来源:igfitidea点击:

How can I access a classmethod from inside a class in Python

pythonclass-members

提问by Volker

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.

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

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

提前致谢,沃尔克

采纳答案by BrenBarn

At the time that x=10is executed in your example, not only does the class not exist, but the classmethod doesn't exist either.

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

Execution in Python goes top to bottom. If x=10is above the classmethod, there is no way you can access the classmethod at that point, because it hasn't been defined yet.

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

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.

即使您可以运行 classmethod,也没关系,因为该类尚不存在,因此 classmethod 无法引用它。直到整个类块运行后才会创建类,因此当您在类块中时,没有类。

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

回答by NlightNFotis

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:

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

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

Test().another_method()返回Hello

回答by ZalewaPL

After re-reading your question carefullythis 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:

这次仔细阅读了你的问题后我可以想到两种解决方案。第一个是应用 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


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.

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

回答by martineau

You can't call aclassmethodin theclassdefinition because the class hasn't been fully defined yet, so there's nothing to pass the method as its firstclsargument...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:

你不能classmethodclass定义中调用 a因为类还没有完全定义,所以没有什么可以将方法作为它的第一个cls参数传递......一个经典的鸡和蛋问题。但是,您可以通过__new__()在元类中重载方法并在创建类后从那里调用 classmethod 来解决此限制,如下所示:

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

回答by Grismar

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_RotateCipherinstead, if you don't want people using it by itself.

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

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 stringwhich the question was missing. And finally added an implementation for the abstract methods, alternatively, could have let VigenereCipher inherit from abc.ABCas well.

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 stringwhich the question was missing. And finally added an implementation for the abstract methods, alternatively, could have let VigenereCipher inherit from abc.ABCas well.