【问题标题】:What does classmethod do except changing self to cls?除了将 self 更改为 cls 之外,classmethod 还能做什么?
【发布时间】:2016-11-14 16:15:10
【问题描述】:

有一个关于classmethodproperty 组合在一起的问题已回答:Using property() on classmethods

我还是不明白问题的原因,请帮忙。

我对@9​​87654324@ 的理解是,它只是将self 替换为cls。考虑到这一点,我在过去几年中编写了几个类方法,现在我发现我一直都错了。

那么从下面的代码看@classmethod@cm有什么区别呢?

def cm(func):
    def decorated(self, *args, **kwargs):
        return func(self.__class__, *args, **kwargs)
    return decorated

class C:
    V = 0 

    @property
    @classmethod
    def inc1(cls):
        cls.V += 1
        print("V1 =", cls.V)

    @property
    @cm 
    def inc3(cls):
        cls.V += 3
        print("V3 =", cls.V)

c = C() 
#c.inc1  # fails with:  TypeError: 'classmethod' object is not callable
c.inc3   # works

inc3cm 有效,但 inc1classmethod 无效。

【问题讨论】:

    标签: python class-method


    【解决方案1】:

    从下面的代码看@classmethod 和@cm 有什么区别?

    在创建实例之前的类创建期间调用装饰器。

    在您的情况下,由于@cm 返回func(self.__class__, *args, **kwargs),它依赖于self,因此应该将其用作实例方法。

    另一方面,@classmethod 可以在实例创建之前使用。

    def cm(func):
        def decorated(self, *args, **kwargs):
            return func(self.__class__, *args, **kwargs)
        return decorated
    
    class C:
        @classmethod
        def inc1(cls):
            (blablabla)
        @cm 
        def inc3(cls):
            (blablabla)
    
    C().inc1() # works as a instance method
    C.inc1()   # works as a classmethod
    C().inc3() # works as a instance method
    C.inc3()   # TypeError: unbound method decorated() must be called with C instance as first argument (got nothing instead)
    

    对于类方法和属性的组合,可以通过返回一个自定义对象来完成。 Reference

    class ClassPropertyDescriptor(object):   
        def __init__(self, f):
            self.f = f
        def __get__(self, obj, klass=None):
            if klass is None:
                klass = type(obj)
            return self.f.__get__(obj, klass)()
    
    def classproperty(func):
        if not isinstance(func, (classmethod, staticmethod)):
            func = classmethod(func)    
        return ClassPropertyDescriptor(func)
    
    class C:
        @classproperty
        def inc1(cls):
            (blablabla)
    
    C.inc1   # works as a classmethod property
    

    [编辑]

    问。 classmethod() 调用对它装饰的方法有什么作用?

    可以使用descriptor实现实现

    class ClassMethodDescriptor(object):    
        def __init__(self, f):
            self.f = f
        def __get__(self, obj, klass=None):
            if klass is None:
                klass = type(obj)
            def newfunc(*args):
                return self.f(klass, *args)
            return newfunc
    
    def myclassmethod(func):
        return ClassMethodDescriptor(func)  
    
    class C:
        @myclassmethod
        def inc1(cls):
            (blablabla)
    
    C.inc1()   # works as a classmethod
    

    问。为什么结果不可调用?

    因为ClassMethodDescriptor的实现没有定义__call__函数。一旦使用@property,它将返回不可调用的ClassMethodDescriptor。

    【讨论】:

    • 您写道,类方法不需要实例。 classmethod() 调用对它装饰的方法有什么作用?为什么结果不可调用?
    • 我更新了我的答案。希望这对您有所帮助。
    • 谢谢,现在更清楚了。 @classmethod 将函数转换为描述符。调用此描述符会返回一个可调用函数。最后缺少的部分是为什么它是这样设计的。我只能猜测主要原因是“调用的细节取决于 obj 是对象还是类。”(来源:docs.python.org 上的 Descriptor HowTo Guide)。这使得区分这两种情况成为可能,从而提供正确的cls 作为第一个参数。正如我所说,只是猜测。
    • This将帮助你理解C++/Java/Python中的staticmethod,并介绍python中classmethod的定义。引用最重要的一句话“当你需要访问类而不是实例时使用@classmethod”
    【解决方案2】:

    区别在于classmethod是不可调用的,而cm方法是可调用的。这意味着当属性(类)调用输入的函数(它应该这样做)时,它会像你一样工作,除了 cm,但对 classmethod 不起作用,因为 classmethod 没有 调用 实施。

    【讨论】:

      【解决方案3】:

      类方法对实例一无所知,也不需要它。 实例方法知道它的实例和它的类。

      class Foo:
          some = 'some'
      
      class Bar(Foo):
          def __init__(self):
              self.some = 'not some'
          @classmethod
          def cls_some(cls):
              print(cls.some)
          def instance_some(self):
              print(self.some)
      
      
      
      Bar.cls_some()
      >>>some
      Bar().instance_some()
      >>>not some
      

      您也可以看到,您不需要实例来调用 classmethod。

      【讨论】:

      • 您已经证明类方法是不同的,因为它们不需要实例。 classmethod() 调用对它装饰的方法有什么作用?为什么结果不可调用?
      猜你喜欢
      • 2017-04-20
      • 2021-04-11
      • 2016-05-07
      • 1970-01-01
      • 2011-11-11
      • 1970-01-01
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      相关资源
      最近更新 更多