关于python:除了将self改为cls之外,classmethod还能做什么?

What does classmethod do except changing self to cls?

有一个关于classmethodproperty组合在一起的回答问题:在类方法上使用property()。

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

我对classmethod的理解是,它只是用cls取代了self。考虑到这一点,在过去的几年里我写了几个类方法,现在我发现我一直都是错的。

那么,@classmethod@cm与下面的代码有什么区别?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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起作用。


what is the difference between @classmethod and @cm from the code below?

在类创建期间,在创建实例之前,decorator正在调用。

在您的例子中,@cm返回func(self.__class__, *args, **kwargs),这依赖于self,因此它应该用作实例方法。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)

对于ClassMethod和属性的组合,可以通过返回自定义对象来完成。参考文献

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

[编辑]

Q. What does the classmethod() call do with the method it decorates to achieve that?

可以通过使用描述符来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

Q. Why is the result not callable?

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


区别在于ClassMethod不可调用,而CM方法可调用。这意味着,当属性(类)调用输入的func(它应该这样做)时,它的工作方式与除了cm以外的其他方式相同,但对classmethod无效,因为classmethod没有实现调用。


类方法不知道任何有关实例的信息,也不需要它。实例方法知道它的实例和类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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。