Reputation: 17247
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.
Upvotes: 2
Views: 363
Reputation: 3080
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.
Upvotes: 2
Reputation: 424
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.
Upvotes: 0
Reputation: 1151
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.
Upvotes: -1