在python中,vars()
检索对象的所有属性。对于从其他类派生的类的实例,是否有方便的方法来过滤掉“继承的”属性?我这样做的目的是在自己的属性上定义__eq__
比较。
根据我的理解,这些属性不是继承的,而是直接存在于对super().__init__()
的调用中的实例中,因此不会有内置的分离机制。
class Base:
def __init__(self):
self.a = 0
class Derived(Base):
def __init__(self):
super().__init__()
self.b = 1
derived = Derived()
# results in {'a': 0, 'b': 1}
# how can I make this return {'b': 1}?
print(vars(derived))
我当前的解决方案有效,但是需要为每个类定义它:
class Derived(Base):
def __init__(self):
super().__init__()
pre_init_attrs = set(vars(self).keys())
# own init
self.b = 1
# process own init
post_init_attrs = set(vars(self).keys())
self._own_attrs = post_init_attrs.difference(pre_init_attrs)
def vars(self):
return {key: self.__dict__[key] for key in self._own_attrs}
是否有一种通过装饰器或元类完成此操作的方法?
答案 0 :(得分:0)
您要的内容与Python中类的工作方式并没有实际意义。
vars
已经做了过滤掉了基类的继承属性。
但是self.a
不是继承的。它已显式添加到您的self
实例中,因为您的__init__
显式调用了super().__init__()
。调用确实没有什么特别的-如果您的__init__
调用了self.addx()
,而Derived.addx
方法执行了self.x = 0
,则它的工作方式与您所做的完全相同在这里。
您可以编写装饰器来完成您要尝试的操作,但是最好退后一步。
为什么不存储您希望每个类作为该类的一部分提供的字段列表?
或者,由于您似乎正在尝试模拟类似Java样式的类的类,其中每个类的属性集都是固定的,所以为什么不使用@dataclass
(或者,如果您需要3.7之前的兼容性)或其他功能(第三方@attr.s
)?这样可以自动跟踪哪些字段属于哪个类,以及与之相关的很多其他事情。
>>> from dataclasses import dataclass, fields
>>>
>>>> @dataclass
... class Base:
... a: int = 0
...
>>> @dataclass
... class Derived(Base):
... b: int = 1
...
>>> derived = Derived()
>>> derived
Derived(a=0, b=1)
>>> def ownfields(obj):
... fs = fields(obj)
... for base in type(obj).__bases__:
... basefs = set(fields(base))
... fs = [f for f in fs if f not in basefs]
... return fs
...
>>> ownfields(derived)
[Field(name='b',type=<class 'int'>,default=1,default_factory=<dataclasses._MISSING_TYPE object at 0x10571ca58>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)]
>>> def ownvars(obj):
... return {field.name: getattr(obj, field.name) for field in ownfields(obj)}
...
>>> ownvars(derived)
{'b': 1}
(使用attrs
代替dataclasses
,您可以有效地完成同一件事-但将其写为attr.asdict
且将filter
参数设为黑名单可能更清楚基类的字段。)
以上代码允许dataclass
类型的多重继承,但不允许dataclass
和非dataclass
的多重继承。如果需要,只需将try
添加到ownfields
:
>>> def ownfields(obj):
... fs = fields(obj)
... for base in type(obj).__bases__:
... try:
... basefs = set(fields(base))
... except TypeError:
... pass
... else:
... fs = [f for f in fs if f not in basefs]
... return fs
例如,如果您从不是数据类本身的多个类继承,而是都以菱形模式从数据类继承,则此方法仍然不起作用。我不确定在这种情况下的预期行为。可能只是跟随type(obj).mro()[1:]
?毫无疑问,这似乎是最一致的查找方法……