关于将classmethodproperty组合在一起的问题已得到解答:Using property() on classmethods

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

我对classmethod的理解是,它只是将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无效。

最佳答案

下面的代码中@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。

关于python - 除将自己更改为cls之外,classmethod还做什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40593183/

10-11 08:25