Using Property() on Classmethods

Using descriptors or properties with classmethod

Starting in Python 3.9, classmethods trigger the descriptor protocol. From the Python docs:

The code path for hasattr(obj, '__get__') was added in Python 3.9 and makes it possible for classmethod() to support chained decorators.

Surprisingly, diving a bit deeper into the subject will show you that classmethod triggers the descriptor __get__ with the class itself as the instance:

class Descriptor:
def __get__(self, instance, owner):
print(instance, owner)
def __set__(self, value, owner):
print(value, owner)

class A:
regular = Descriptor()
clsmethod = classmethod(Descriptor())

>>> A.regular
None <class '__main__.A'>
>>> A.clsmethod
<class '__main__.A'> None

I'm guessing they made it specifically to support descriptors such as @property, as accessing them through the class returns the property itself:

class B:
@property
def prop(self):
print(self)

>>> B.__dict__["prop"].__get__(None, 1234)
<property object at 0x000001BEEB635630>
>>> B.__dict__["prop"].__get__(1234, None)
1234

It's a bit unintuitive and turns the descriptor protocol clunky if you wish to support both classmethod and normal descriptors, as you have to check if the owner is None.

Keep in mind however, __set__ is not called (as the descriptor protocol doesn't call it when setting class attributes), making you unable to use @property.setter:

>>> A.regular = 1234
>>> A.regular
1234
>>> A.clsmethod = 1234
>>> A.clsmethod
1234

How to apply properties on classmethod?

This is possible in python >= 3.9 per https://docs.python.org/3/howto/descriptor.html#id27
See For example, a classmethod and property could be chained together

You may have a use case where you only want to calculate a classmethod property one time then keep using that value. If that is the case and this calculation needs to be done on subclasses, one could use init_subclass in python >= 3.6 to do this

class A:
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.prix = cls._prix

class B(A):
_prix = 0

Static class method properties in Python

An instance's properties must be defined on its class. To define properties on a class, the rule still holds: when considering the class as an instance, its properties must be defined on the class's class (its metaclass).

class Meta(type):

@property
def x(self):
return self._x

@x.setter
def x(self, n):
self._x = n

class Foo(metaclass=Meta):
_x = 0

Despite the drawbacks of using metaclasses, this is the way that works.

Python 3.9 MetaClass Property vs Classmethod Property

I think this has nothing to do with properties, metaclasses or abc.

A simple example:

>>> int.__mro__
(<class 'int'>, <class 'object'>)
>>> isinstance(int, type)
True
>>> '__mro__' in dir(int)
False
>>> '__mro__' in dir(type)
True

In this example, object is the base class of int, while type is the metaclass of int.

The official documentation of the dir function explicitly says that it

attempts to produce the most relevant, rather than complete, information

and

If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.

and

metaclass attributes are not in the result list when the argument is a class

what's the difference between property and class method?

The property is called each time you access product.total_ammount_in_store, not at the time when the product is created.

Therefore including {{ product.total_ammount_in_store }} in your template will do the right thing.

By using the property decorator, you can access product.total_ammount_in_store instead of product.total_ammount_in_store() if it was an instance method. In the Django template language, this difference is not so apparent, because Django will call the method automatically in the template.

Don't confuse an instance method with a class method, which is quite different. A class method belongs to your class Product, not an individual instance product. You don't have access to instance variables e.g. self.package when you call a class method.

how to use @ in python.. and the @property and the @classmethod

A decorator needs to be a callable object (either a function or an object implementing __call__), where the parameter is the function that has been decorated, and the result is a function that will replace the function that has been decorated, so, to use your example of printing 'sss' instead of printing 'aaa':


>>> def a(f):
... def replacementfunc():
... print 'sss'
... return replacementfunc;
...
>>> @a
... def b():
... print 'aaa'
...
>>> b()
sss

Or, a more elaborate example:


>>> class print_decorator(object):
... def __init__(self,text):
... self.text = text;
... def __call__(self,f):
... def replacement():
... print self.text;
... return replacement;
...
>>> @print_decorator("Hello world!")
... def b():
... print 'aaa';
...
>>> b()
Hello world!

Edit

As for your updated question, you need to look at the documentation for @property. It's not clear exactly what you are trying to accomplish, although my guess is that you want:


class a:
@property
def b(self):
return 'sss'

aa=a()
print aa.b # prints 'sss', whereas without @property, prints <function ... >


Related Topics



Leave a reply



Submit