Python (3) class 属性 古怪
Python (3) class property weirdness
注意 根据要求更新为更短的代码和问题。希望这会有所帮助。
我正在尝试了解可以创建 class 属性的各种方式(例如,对于实例属性,如 @属性,但对于 class attributes/variables ).我已经尝试了这里的一些建议(例如 Using property() on classmethods and How to make a class property?)。
简而言之,看起来使用为“Using property() on classmethods”中的 Python 3.x 推荐的元class 方法导致属性实际上没有被保留(见测试结果)。我想知道我是否犯了错误,或者是否有人可以解释为什么我看到的是预期的正确行为。
请注意,来自“How to make a class property?" seems to behave as I would expect, but I tested the "best" answer in "How to make a class property?”的代码并不像我预期的那样运行。
代码
为了解决问题,我为 Python 3.x:
指定的元 class 方法编写了一些测试代码
class SomeClassMeta(type):
meta_attr = "SomeClassMeta Default meta_attr"
# From
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
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
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
return 是 property()
而所有其他 property()
是 class 还是实例预期(甚至在第一次实例化之后)?
- 我认为 metaclass 的目的是创建一个 Class 属性。为什么设置
Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
会更改实例值而不是 class 值?请注意,Some_Inst.class_prop 的行为与我想象的一样。
由于您对问题进行了实质性更改,这里有一个新答案。
- Some_Inst.inst_prop = 'Set with Some_Inst.inst_prop', Some_Inst.__class__.inst_prop = '<property object at 0x000002062A3C9B38>'
作为实例 Some_Inst
的 属性,评估 Some_Inst.inst_prop
会得到 属性 值,而评估 Some_Inst.__class__.inst_prop
会得到 [=14] =] 用于 class:一个 属性。它是在 class 上定义的 属性,因此它将解析为实例的值和 class.
的 属性
在第一个问题的上下文中,norm_prop
也是如此。
关于第二个问题:"I thought the purpose of the metaclass was to create a Class property. Why then does setting Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
change the instance value but not the class value?"
因为 Some_class
在您用 Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
赋值之前没有属性 meta_prop
。 meta_prop
是您在 meta-class 上定义的 属性,但由于 SomeClass
不是从 SomeClassMeta
继承的(它只是将其作为元class), 它不在 SomeClass
.
为什么要使用 meta-classes 来定义 class-properties,为什么不简单地在 class 上定义它们?用蒂姆·彼得斯的话来说:
"Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why)."
这篇文章有很好的进一步解释:https://realpython.com/python-metaclasses/
注意 根据要求更新为更短的代码和问题。希望这会有所帮助。
我正在尝试了解可以创建 class 属性的各种方式(例如,对于实例属性,如 @属性,但对于 class attributes/variables ).我已经尝试了这里的一些建议(例如 Using property() on classmethods and How to make a class property?)。
简而言之,看起来使用为“Using property() on classmethods”中的 Python 3.x 推荐的元class 方法导致属性实际上没有被保留(见测试结果)。我想知道我是否犯了错误,或者是否有人可以解释为什么我看到的是预期的正确行为。
请注意,来自“How to make a class property?" seems to behave as I would expect, but I tested the "best" answer in "How to make a class property?”的代码并不像我预期的那样运行。
代码
为了解决问题,我为 Python 3.x:
指定的元 class 方法编写了一些测试代码class SomeClassMeta(type):
meta_attr = "SomeClassMeta Default meta_attr"
# From
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
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
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
return 是property()
而所有其他property()
是 class 还是实例预期(甚至在第一次实例化之后)? - 我认为 metaclass 的目的是创建一个 Class 属性。为什么设置
Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
会更改实例值而不是 class 值?请注意,Some_Inst.class_prop 的行为与我想象的一样。
由于您对问题进行了实质性更改,这里有一个新答案。
- Some_Inst.inst_prop = 'Set with Some_Inst.inst_prop', Some_Inst.__class__.inst_prop = '<property object at 0x000002062A3C9B38>'
作为实例 Some_Inst
的 属性,评估 Some_Inst.inst_prop
会得到 属性 值,而评估 Some_Inst.__class__.inst_prop
会得到 [=14] =] 用于 class:一个 属性。它是在 class 上定义的 属性,因此它将解析为实例的值和 class.
在第一个问题的上下文中,norm_prop
也是如此。
关于第二个问题:"I thought the purpose of the metaclass was to create a Class property. Why then does setting Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
change the instance value but not the class value?"
因为 Some_class
在您用 Some_Inst.meta_prop = "Set with Some_Inst.meta_prop"
赋值之前没有属性 meta_prop
。 meta_prop
是您在 meta-class 上定义的 属性,但由于 SomeClass
不是从 SomeClassMeta
继承的(它只是将其作为元class), 它不在 SomeClass
.
为什么要使用 meta-classes 来定义 class-properties,为什么不简单地在 class 上定义它们?用蒂姆·彼得斯的话来说:
"Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why)."
这篇文章有很好的进一步解释:https://realpython.com/python-metaclasses/