仅使用vars()

时间:2018-08-03 17:33:05

标签: python python-3.x class attributes

在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}

是否有一种通过装饰器或元类完成此操作的方法?

1 个答案:

答案 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:]?毫无疑问,这似乎是最一致的查找方法……