【问题标题】:How can I access a classmethod from inside a class in Python如何从 Python 中的类内部访问类方法
【发布时间】:2020-11-06 21:17:18
【问题描述】:

我想在 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

感谢任何帮助!

提前致谢, 沃尔克

【问题讨论】:

  • 忽略我的大脑打嗝!你有什么理由要这样做吗?
  • 嗯,对我来说重要的是我以后可以再次初始化。当然,仅 x=10 是没有意义的,但在真正的应用程序中。我将加载一些可以在应用程序运行期间更改的 XML 文档。

标签: python class-members


【解决方案1】:

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

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
   }

}

【讨论】:

    【解决方案2】:

    带有 add() @classmethod 的person类

    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
    

    【讨论】:

      【解决方案3】:

      您不能在class 定义中调用classmethod,因为该类尚未完全定义,因此没有什么可以将方法作为其第一个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
      

      输出:

      Hello
      10
      

      【讨论】:

        【解决方案4】:

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

        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

        【讨论】:

          【解决方案5】:

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

          Python 中的执行从上到下。如果x=10 位于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
          

          【讨论】:

          • 哇,这是一个优雅的解决方案!我已经开始使用模块级函数来解决我的问题,但这是一种通用方法!
          • @user1907681 我不相信你想走这条路,但是我不知道你的确切问题,所以不能确定......但是,这绝对是实现这一点的最 Pythonic 方式(有很好的解释)... +1
          • 最好从 init 调用 initStuff 而不是依靠创建者来调用它。这解决了原始海报的“# HERE I WOULD LOVE TO CALL SOMEHOW static_init!”要求。
          • @steveayre:从__init__ 调用它意味着每个实例都会调用它。他希望全班都调用一次。
          【解决方案6】:

          这次仔细重新阅读了您的问题后,我想到了两个解决方案。第一个是应用 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 的答案是更好的:在访问类方法时使用类名。它使您的代码不那么晦涩。

          【讨论】:

          • 不幸的是,这并没有解决我的问题。我知道如何从类的外部访问类方法。但我想有一些类似于 Java 中的静态初始化器的东西,它调用一个可以稍后再次调用的静态方法。
          • 研究 Borg 设计模式。
          • 是的,Borg 设计模式或其他单例模式解决方案也可以解决我的问题。
          • '它使你的代码不那么晦涩' 它还破坏了子类化以覆盖类方法。
          【解决方案7】:

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

          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

          【讨论】:

          • 不幸的是,这似乎只是在课堂外工作。我刚刚尝试过: class Test(): # static member x = None Test.static_init() # 在 classmethod 中初始化静态成员,以便稍后可以再次 #reinitialized @classmethod def static_init(cls): cls.x = 10 if name == "main": print(Test.x) 结果: Test.static_init() NameError: name 'Test' is not defined#
          • 当我尝试在类内部使用 Test.static_init() 或 static_init(Test) 并且在定义类方法之前出现错误:NameError: name 'static_init' is not defined
          • @user1907681 是的,伙计,这是因为 python 遵循自上而下的方法。这意味着,如果您尝试在实际 define 之前执行 Test.static_init(),那么您的程序的其余部分将不知道 static_init(),因此获得 Name Error 是正常的。
          • 好吧,我也尝试将代码放在类方法的定义之后。但是我有相同的 NameError: class Test(): # static member x = None # 在 classmethod 中初始化静态成员,以便稍后可以再次 #reinitialized @classmethod def static_init(cls): cls.x = 10 Test.static_init()
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-15
          • 1970-01-01
          • 1970-01-01
          • 2011-01-02
          相关资源
          最近更新 更多