【发布时间】:2020-06-05 08:08:41
【问题描述】:
注意根据要求更新为更短的代码和问题。希望这会有所帮助。
我试图了解创建类属性的各种方式(例如,@property 用于实例属性,但用于类属性/变量)。我已经尝试了这里的一些建议(例如,Using property() on classmethods 和 How to make a class property?)。
简而言之,看起来使用“Using property() on classmethods”中为 Python 3.x 推荐的元类方法会导致属性实际上没有被保留(参见测试结果)。我想知道我是否犯了一个错误,或者是否有人可以解释为什么我看到的是预期和正确的行为。
请注意,来自“How to make a class property?”的代码的行为似乎符合我的预期,但我测试了“How to make a class property?”中的“最佳”答案,但它的行为与我预期的不符。
代码
为了弄清楚,我为 Python 3.x 指定的元类方法编写了一些测试代码:
class SomeClassMeta(type):
meta_attr = "SomeClassMeta Default meta_attr"
# From https://stackoverflow.com/questions/128573/using-property-on-classmethods
def __init__(self, *args, **kwargs):
self.meta_attr = "SomeClassMeta.__init__() set meta_attr"
pass
@property
def meta_prop(self):
return self.meta_attr
@meta_prop.setter
def meta_prop(self, val):
self.meta_attr = val
pass
class ClasspropertyDescriptor(object):
# From https://stackoverflow.com/questions/5189699/how-to-make-a-class-property
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
"""Get it."""
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
"""Set it."""
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
"""Set some class value."""
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
# From https://stackoverflow.com/questions/5189699/how-to-make-a-class-property
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
pass
return ClasspropertyDescriptor(func)
class ClasspropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClasspropertyDescriptor:
return obj.__set__(self, value)
return super(ClasspropertyMetaClass, self).__setattr__(key, value)
class SomeClass(metaclass=SomeClassMeta):
class_attr = "SomeClass Default class_attr"
norm_attr = "SomeClass Default norm_attr"
inst_attr = "SomeClass Default inst_attr"
_name = "SomeClass"
def __init__(self,name):
"""Init this."""
self.inst_attr = "SomeClass.__init__() set inst_attr"
self._name = name
pass
@property
def norm_prop(self):
return self.norm_attr
@norm_prop.setter
def norm_prop(self, val):
self.norm_attr = val
pass
@classproperty
def class_prop(self):
return self.class_attr
@class_prop.setter
def class_prop(self, val):
self.class_attr = val
pass
@property
def inst_prop(self):
"""Get the instance variable (attribute)."""
return self.inst_attr
@inst_prop.setter
def inst_prop(self, val):
self.inst_attr = val
pass
def _info(self,attr):
attrval = getattr(self,attr,'No Such Attribute')
attrcval = getattr(self.__class__,attr,'No Such Attribute')
print(f" - {self._name}.{attr} = '{attrval}', {self._name}.__class__.{attr} = '{attrcval}'")
if isinstance(attrval,property):
print(f" - {self._name}.{attr}.__get__() = '{attrval.__get__(self)}'")
def info(self):
print(f"{self._name} is a {type(self).__name__}")
for attr in [
'class_prop',
'class_attr',
'inst_attr',
'inst_prop',
'meta_attr',
'meta_prop',
'norm_attr',
'norm_prop',
]:
self._info(attr)
self.__class__._info(self.__class__,attr)
Some_Inst = SomeClass('Some_Inst')
Some_Inst.class_prop = "Set with Some_Inst.class_prop"
Some_Inst.inst_prop = "Set with Some_Inst.inst_prop"
Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
Some_Inst.norm_prop = "Set with Some_Inst.norm_prop"
Some_Inst.info()
输出
Some_Inst is a SomeClass
- Some_Inst.class_prop = 'Set with Some_Inst.class_prop', Some_Inst.__class__.class_prop = 'Set with Some_Inst.class_prop'
- SomeClass.class_prop = 'Set with Some_Inst.class_prop', SomeClass.__class__.class_prop = 'No Such Attribute'
- Some_Inst.class_attr = 'Set with Some_Inst.class_prop', Some_Inst.__class__.class_attr = 'Set with Some_Inst.class_prop'
- SomeClass.class_attr = 'Set with Some_Inst.class_prop', SomeClass.__class__.class_attr = 'No Such Attribute'
- Some_Inst.inst_attr = 'Set with Some_Inst.inst_prop', Some_Inst.__class__.inst_attr = 'SomeClass Default inst_attr'
- SomeClass.inst_attr = 'SomeClass Default inst_attr', SomeClass.__class__.inst_attr = 'No Such Attribute'
- Some_Inst.inst_prop = 'Set with Some_Inst.inst_prop', Some_Inst.__class__.inst_prop = '<property object at 0x7fdc48c594a8>'
- SomeClass.inst_prop = '<property object at 0x7fdc48c594a8>', SomeClass.__class__.inst_prop = 'No Such Attribute'
- SomeClass.inst_prop.__get__() = 'SomeClass Default inst_attr'
- Some_Inst.meta_attr = 'SomeClassMeta.__init__() set meta_attr', Some_Inst.__class__.meta_attr = 'SomeClassMeta.__init__() set meta_attr'
- SomeClass.meta_attr = 'SomeClassMeta.__init__() set meta_attr', SomeClass.__class__.meta_attr = 'SomeClassMeta Default meta_attr'
- Some_Inst.meta_prop = 'Set with Some_Inst.meta_prop', Some_Inst.__class__.meta_prop = 'SomeClassMeta.__init__() set meta_attr'
- SomeClass.meta_prop = 'SomeClassMeta.__init__() set meta_attr', SomeClass.__class__.meta_prop = '<property object at 0x7fdc48c59908>'
- Some_Inst.norm_attr = 'Set with Some_Inst.norm_prop', Some_Inst.__class__.norm_attr = 'SomeClass Default norm_attr'
- SomeClass.norm_attr = 'SomeClass Default norm_attr', SomeClass.__class__.norm_attr = 'No Such Attribute'
- Some_Inst.norm_prop = 'Set with Some_Inst.norm_prop', Some_Inst.__class__.norm_prop = '<property object at 0x7fdc48c596d8>'
- SomeClass.norm_prop = '<property object at 0x7fdc48c596d8>', SomeClass.__class__.norm_prop = 'No Such Attribute'
- SomeClass.norm_prop.__get__() = 'SomeClass Default norm_attr'
问题
- 为什么
SomeClass.inst_prop和SomeClass.norm_prop返回property()而所有其他property()s 无论是在类还是实例上都按预期运行(即使在第一次实例化之后)? - 我认为元类的目的是创建一个 Class 属性。那么为什么设置
Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"会改变实例值而不是类值呢?请注意,Some_Inst.class_prop 的行为与我想象的一样。
【问题讨论】:
-
你有没有机会把它剪掉一点?
-
我的上帝。发的太多了。请参阅minimal reproducible example。很明显你已经付出了很多努力,但我不会经历所有这些
-
我会尽快编辑。
-
下面回答,但是除了多余的代码和例子,你为什么
passing这么多? -
@Grismar 我使用 pass 因为在协作时没有它,一些尝试重新格式化的工具会导致问题。我正在更新问题以更紧凑。
标签: python python-3.x properties python-decorators metaclass