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 forclassmethod()
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
Why Can't Python Sockets Resolve Url's with Http in It
Python Equivalent of Ruby's 'Method_Missing'
Is There a Built-In Function to Print All the Current Properties and Values of an Object
CSV in Python Adding an Extra Carriage Return, on Windows
Animated Sprite from Few Images
Why Does This Code for Initializing a List of Lists Apparently Link the Lists Together
Pip How to Remove Incorrectly Installed Package with a Leading Dash: "-Pkgname"
Dynamically Updating Plot in Matplotlib
Threading.Timer - Repeat Function Every 'N' Seconds
Add a Prefix to All Flask Routes
Passing Variable from Python Script to Bash Script
How to Open (Read-Write) or Create a File with Truncation Allowed
Removing Duplicates from a List of Lists
Error: Pandas Hashtable Keyerror
Matplotlib: How to Create Axessubplot Objects, Then Add Them to a Figure Instance