2

There is an answered question about classmethod and property combined together: Using property() on classmethods

I still don't understand the cause of the problem, please help.

My understanding of classmethod was that it simply replaces self with cls. With this in mind I wrote several classmethods during the past few years and now I see I was wrong all that time.

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

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

inc3 with cm works, but inc1 with classmethod does not.

Community
  • 1
  • 1
VPfB
  • 14,927
  • 6
  • 41
  • 75

3 Answers3

2

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

decorator is calling during class creation time before an instance is created.

In your case, since @cm returns func(self.__class__, *args, **kwargs), which is relied on self, it should be used as a instance method.

On the other hand, @classmethod is able to use before an instance is created.

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)

For a combination of classmethod and property, it could be done by return an customized object. 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

[Edit]

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

The implementation can be done by using 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

Q. Why is the result not callable?

Because the implementation of ClassMethodDescriptor does not define __call__ function. Once using @property, it will return ClassMethodDescriptor which is not callable.

Community
  • 1
  • 1
Kir Chou
  • 2,980
  • 1
  • 36
  • 48
  • You wrote that classmethods don't need an instance. What does the classmethod() call do with the method it decorates to achieve that? Why is the result not callable? – VPfB Nov 15 '16 at 09:09
  • I updated my answer. Hope this will be helpful for you. – Kir Chou Nov 15 '16 at 11:26
  • Thank you, it is clearer now. `@classmethod` transforms a function to a descriptor. Invoking this descriptors returns a callable function. Last missing piece is why it was designed this way. I can only guess that the main reason is "_The details of invocation depend on whether obj is an object or a class._" (source: Descriptor HowTo Guide on docs.python.org). This makes it possible to distinguish these two cases and thus provide the right `cls` as the first argument. As I said, just guessing. – VPfB Nov 15 '16 at 14:11
  • [This](http://stackoverflow.com/a/15031999/2740386) will help you to understand the staticmethod in C++/Java/Python and introduce the definition of classmethod in python. Quote the most important sentence "use a @classmethod when you need access to the class, but not the instance" – Kir Chou Nov 15 '16 at 14:20
0

The difference is that classmethod is not callable, and cm method is callable. This means that when the property(class) makes a call to the inputed func(which it is supposed to do), it works as you'll except for cm, but will not work for classmethod since classmethod does not have a call implemented.

Simon
  • 424
  • 4
  • 12
-1

class method does not know anything about instance and does not require it. instance method knows about it's instance and it's class.

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

Also as you can see you don't need an instance to call classmethod.

Alex
  • 1,141
  • 8
  • 13
  • You have shown that classmethods are different, because they don't need an instance. What does the classmethod() call do with the method it decorates to achieve that? Why is the result not callable? – VPfB Nov 15 '16 at 09:08