Python:Class 属性,默认为 parent 的值并且可用于整个层次结构
Python: Class attribute which defaults to parent's value and is collectible for entire hierarchy
这个问题很难用语言表达。我希望标题正确捕捉它。
我在找什么:
class Parent():
x = "P"
class ChildA(Parent):
x = "A"
class ChildB(Parent):
# not setting x
pass
有了这个,下面的代码应该完全像看到的那样工作:
>>> Parent.x
'P'
>>> ChildA.x
'A'
>>> ChildB.x
'P'
>>> ChildB.x = 'B'
>>> ChildB.x
'B'
到目前为止没有什么特别的,但这里是棘手的地方,因为应该保留 parent 的属性值:
>>> ChildB.x = None
>>> ChildB.x
'P'
另外,我需要这个才能工作:
>>> ChildA.get_x_list()
['A', 'P']
>>> ChildB.get_x_list()
['P'] # 'P' only appears once
我最近读了很多关于 meta类 的文章,我认为这最好通过使用一个来完成:
has__attr = lambda obj, name: hasattr(obj, "_" + obj.__name__ + "__" + name)
get__attr = lambda obj, name: getattr(obj, "_" + obj.__name__ + "__" + name)
set__attr = lambda obj, name, value: setattr(obj, "_" + obj.__name__ + "__" + name, value)
del__attr = lambda obj, name: delattr(obj, "_" + obj.__name__ + "__" + name)
class Meta(type):
the_parent_name = "mParent"
def __new__(cls, class_name, bases, attributes):
parent_attribute_id = "_" + class_name + "__" + cls.the_parent_name
x = attributes.pop("x", None)
if x:
attributes["_" + class_name + "__x"] = x
# build line of inheritance
for b in bases:
# find a base that has the parent attribute
if has__attr(b, cls.the_parent_name):
# set the parent attribute on this class
attributes[parent_attribute_id] = b
break
else:
# add the parent attribute to this class, making it an inheritance root
attributes[parent_attribute_id] = None
return super(Meta, cls).__new__(cls, class_name, bases, attributes)
def get_x_list(self):
ls = [get__attr(self, "x")] if has__attr(self, "x") else []
parent = get__attr(self, self.the_parent_name)
if parent:
ls.extend(parent.get_x_list())
return ls
@property
def x(self):
if has__attr(self, "x"):
return get__attr(self, "x")
else:
parent = get__attr(self, self.the_parent_name)
if parent:
return parent.x
else:
return None
@x.setter
def x(self, x):
if x:
set__attr(self, "x", x)
else:
del__attr(self, "x")
虽然这已经完全按预期工作,但我想知道使用元类是否过头了,实际上是否有更简单的方法来做到这一点?
重要提示: 我需要以一种绝对不会在派生 类 中完成任何特殊操作的方式来完成此操作。 x = "Q"
和 set_x("Q")
是可以接受的。这是一项要求,因为我正在设计 API,其中 Parent
是库的一部分,派生的 类 不在我的手中。
额外问题:有没有办法让属性的名称 ("x") 仅在一个位置可变?含义:是否可以通过字符串创建get_x_list
和x
属性?我想象的是:
attributes["get_" + attr_name + "_list"] = ...
但每当我尝试这样做时,我得到:
... missing 1 required positional argument: 'self'
对于这些功能,您不需要元类。
>>> ChildB.x = None
>>> ChildB.x
'P'
只需将第一行更改为 del ChildB.x
即可。
>>> ChildA.get_x_list()
['A', 'P']
>>> ChildB.get_x_list()
['P'] # 'P' only appears once
试试这个:
(klass.__dict__['x'] for klass in ChildA.__mro__ if 'x' in klass.__dict__)
如果您需要列表而不是生成器,请将最外面的一对括号更改为方括号。
这个问题很难用语言表达。我希望标题正确捕捉它。
我在找什么:
class Parent():
x = "P"
class ChildA(Parent):
x = "A"
class ChildB(Parent):
# not setting x
pass
有了这个,下面的代码应该完全像看到的那样工作:
>>> Parent.x
'P'
>>> ChildA.x
'A'
>>> ChildB.x
'P'
>>> ChildB.x = 'B'
>>> ChildB.x
'B'
到目前为止没有什么特别的,但这里是棘手的地方,因为应该保留 parent 的属性值:
>>> ChildB.x = None
>>> ChildB.x
'P'
另外,我需要这个才能工作:
>>> ChildA.get_x_list()
['A', 'P']
>>> ChildB.get_x_list()
['P'] # 'P' only appears once
我最近读了很多关于 meta类 的文章,我认为这最好通过使用一个来完成:
has__attr = lambda obj, name: hasattr(obj, "_" + obj.__name__ + "__" + name)
get__attr = lambda obj, name: getattr(obj, "_" + obj.__name__ + "__" + name)
set__attr = lambda obj, name, value: setattr(obj, "_" + obj.__name__ + "__" + name, value)
del__attr = lambda obj, name: delattr(obj, "_" + obj.__name__ + "__" + name)
class Meta(type):
the_parent_name = "mParent"
def __new__(cls, class_name, bases, attributes):
parent_attribute_id = "_" + class_name + "__" + cls.the_parent_name
x = attributes.pop("x", None)
if x:
attributes["_" + class_name + "__x"] = x
# build line of inheritance
for b in bases:
# find a base that has the parent attribute
if has__attr(b, cls.the_parent_name):
# set the parent attribute on this class
attributes[parent_attribute_id] = b
break
else:
# add the parent attribute to this class, making it an inheritance root
attributes[parent_attribute_id] = None
return super(Meta, cls).__new__(cls, class_name, bases, attributes)
def get_x_list(self):
ls = [get__attr(self, "x")] if has__attr(self, "x") else []
parent = get__attr(self, self.the_parent_name)
if parent:
ls.extend(parent.get_x_list())
return ls
@property
def x(self):
if has__attr(self, "x"):
return get__attr(self, "x")
else:
parent = get__attr(self, self.the_parent_name)
if parent:
return parent.x
else:
return None
@x.setter
def x(self, x):
if x:
set__attr(self, "x", x)
else:
del__attr(self, "x")
虽然这已经完全按预期工作,但我想知道使用元类是否过头了,实际上是否有更简单的方法来做到这一点?
重要提示: 我需要以一种绝对不会在派生 类 中完成任何特殊操作的方式来完成此操作。 x = "Q"
和 set_x("Q")
是可以接受的。这是一项要求,因为我正在设计 API,其中 Parent
是库的一部分,派生的 类 不在我的手中。
额外问题:有没有办法让属性的名称 ("x") 仅在一个位置可变?含义:是否可以通过字符串创建get_x_list
和x
属性?我想象的是:
attributes["get_" + attr_name + "_list"] = ...
但每当我尝试这样做时,我得到:
... missing 1 required positional argument: 'self'
对于这些功能,您不需要元类。
>>> ChildB.x = None
>>> ChildB.x
'P'
只需将第一行更改为 del ChildB.x
即可。
>>> ChildA.get_x_list()
['A', 'P']
>>> ChildB.get_x_list()
['P'] # 'P' only appears once
试试这个:
(klass.__dict__['x'] for klass in ChildA.__mro__ if 'x' in klass.__dict__)
如果您需要列表而不是生成器,请将最外面的一对括号更改为方括号。