2016-11-14 55 views
1

有一個回答問題約classmethodproperty結合在一起:Using property() on classmethods除了將自己更改爲cls,classmethod會做什麼?

我還是不明白這個問題的原因,請大家幫忙。

我對classmethod的理解是,它只是用cls代替self。考慮到這一點,我在過去幾年裏寫了幾個classmethods,現在我發現我一直都是錯的。

那麼@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沒有。

回答

2

@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 

[編輯]

問:什麼是類方法()調用做的方法是裝飾來實現呢?

實現可以通過使用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。

+0

你寫過那個classmethods不需要一個實例。 classmethod()調用使用它所實現的方法進行修飾的方法是什麼?爲什麼結果不可調用? – VPfB

+0

我更新了我的答案。希望這會對你有所幫助。 –

+0

謝謝,現在更清楚了。 '@ classmethod'將函數轉換爲描述符。調用這個描述符返回一個可調用的函數。最後缺失的部分是爲什麼它是這樣設計的。我只能猜測主要原因是「_調用的細節取決於obj是一個對象還是一個class._」(來源:docs.python.org上的Descriptor HowTo指南)。這使得區分這兩種情況成爲可能,因此提供了正確的「cls」作爲第一個參數。正如我所說,只是猜測。 – VPfB

-1

類方法不知道有關實例的任何內容,並且不需要它。 實例方法知道它是實例,它是類。

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。

+0

您已經證明,classmethods是不同的,因爲它們不需要實例。 classmethod()調用使用它所實現的方法進行修飾的方法是什麼?爲什麼結果不可調用? – VPfB

0

區別在於classmethod不可調用,cm方法可調用。這意味着當屬性(類)調用輸入的func(它應該這樣做)時,它的工作原理與cm相同,但對classmethod不起作用,因爲classmethod沒有調用實現。