为啥元类适用于类属性但@classmethod @property 不适用?
Posted
技术标签:
【中文标题】为啥元类适用于类属性但@classmethod @property 不适用?【英文标题】:Why metaclass works for a class propery but a @classmethod @property doesn't?为什么元类适用于类属性但@classmethod @property 不适用? 【发布时间】:2021-12-24 16:18:42 【问题描述】:这是 Django deb 模型的简化重新实现。
这很有效(类似于 Django 所做的,虽然它是一个属性,而不是一个属性):
class ClassPropertyType(type):
@property
def objects(cls):
return cls.init_empty_storage_if_needed()
class Model(metaclass=ClassPropertyType):
@classmethod
def init_empty_storage_if_needed(cls):
if not hasattr(cls, "__objects_storage"):
cls.__objects_storage = ObjectStorage(cls)
return cls.__objects_storage
class ObjectStorage:
def __init__(self, model_class):
pass
def all(self):
pass
Model.objects.all()
这个:
class Model:
@classmethod
def init_empty_storage_if_needed(cls):
if not hasattr(cls, "__objects_storage"):
cls.__objects_storage = ObjectStorage(cls)
return cls.__objects_storage
@classmethod
@property
def objects(cls):
return cls.init_empty_storage_if_needed()
class ObjectStorage:
def __init__(self, model_class):
pass
def all(self):
pass
Model.objects.all()
给出一个例外:
Traceback (most recent call last):
File "classmethod_property.py", line 21, in <module>
Model.objects.all()
AttributeError: 'property' object has no attribute 'all'
我想使用更直观的@classmethod+@property 方式。如何让它做同样的事情?
【问题讨论】:
【参考方案1】:您的代码确实有效 -
只是 classmethod
在最近的 Python 中被修改为与 property
很好地配合(我认为只有在 3.10 中)。
在此之前,如果您认为,property
装饰器将方法转换为与方法截然不同的对象:如果没有特定代码,“classmethod”就无法以透明的方式使用它。
在底层,发生的是:对于类方法和属性,
从类中检索属性时,例如在Model.objects
中,Python 运行时检查绑定到Model
类主体上的objects
的对象是否具有__get__
方法。 (property
、classmethod
和函数对象都实现了这一点)。使用“instance=None, owner=Model”参数调用。获取“instance=None”时的常规“属性”将返回自身:这是检索属性的常规方式。在较新的 Python 中,类方法会检查它是在装饰属性,而不是常规函数,然后进行适当的调用。
至于“元类” - 元类中的方法或属性只是元类实例(元类'类)的常规方法或属性 - property
不会像装饰时发生的那样包装或转换它与classmethod
。这就是它在旧版 Python 中工作的原因:使用普通语言机制 - 使用 instance=Model, owner=MetaModel
调用 MetaModel.objects
属性以获得 Model.objects
访问,然后将该调用转换为原始 objects
函数的调用将实例 (Model
) 作为第一个参数 (self
),就像常规的“实例”属性一样。
【讨论】:
以上是关于为啥元类适用于类属性但@classmethod @property 不适用?的主要内容,如果未能解决你的问题,请参考以下文章