列出命名元组子类的属性

6 投票
3 回答
3440 浏览
提问于 2025-04-18 00:37

我有一个小类,它是从 namedtuple 扩展出来的,但它的实例的 __dict__ 属性总是返回空的。

Point = namedtuple('Point', 'x y')
p1 = Point(20, 15)
print(p1, p1.__dict__)
# Point(x=20, y=15) OrderedDict([('x', 20), ('y', 15)]) <--- ok

class SubPoint(Point): pass
p2 = SubPoint(20, 15)
print(p2, p2.__dict__)
# SubPoint(x=20, y=15) {} <--- why is it empty?

p2 有属性,但它的 __dict__ 是空的。不过,用 dir() 查看时,属性是正确列出来的,这点很奇怪。注意,当 SubPoint 从普通类扩展时,这个问题就不存在了。

这是怎么回事,我该如何在我的子类实例中列出属性呢?

3 个回答

0

在编程中,有时候我们会遇到一些问题,特别是在使用某些工具或库的时候。比如,有人可能在使用一个叫做“库”的东西时,发现它的某些功能没有按照预期工作。这种情况可能会让人感到困惑,因为我们不知道问题出在哪里。

通常,解决这些问题的第一步是仔细检查代码,看看有没有写错的地方。接着,可以查阅相关的文档,看看这个库的使用方法是否正确。有时候,文档里会有示例代码,帮助我们理解如何正确使用这个库。

如果自己解决不了,也可以去一些技术论坛,比如StackOverflow,向其他人请教。在提问的时候,最好把自己的代码和遇到的问题详细描述清楚,这样别人才能更好地帮助你。

总之,遇到问题时,不要急于放弃,耐心寻找解决方案,通常都能找到答案。

list(A.__annotations__.keys())
4

问题在于,__slots__ 只对定义它的类有效,所以基类总是会有自己的 __dict__ 属性,除非你在基类中也定义 __slots__。另外,namedtuple__dict__ 属性并不是一个普通的字典,而是一个 @property

根据 文档

一个 __slots__ 声明的作用仅限于定义它的类。因此,子类会有一个 __dict__,除非它们也定义了 __slots__(而且这个 __slots__ 只能包含额外的槽名称)。

所以,当你在子类中定义了 __slots__ 后,它就无法在这个类中找到 __dict__ 属性,于是就去基类找,结果找到了 __dict__ 属性。

一个简单的示例:

class A:
    __slots__=  ('a', 'b')

    @property
    def __dict__(self):
        print ('inside A')
        return self.__slots__         

class B(A):
    pass

print(B().__dict__)

print ('-'*20)

class B(A):
    __slots__ = ()
print(B().__dict__)

输出:

{}
--------------------
inside A
()
1

要了解为什么 __dict__ 不起作用,可以查看 Ashwini Chaudhary 的回答。这个回答讲解了问题的第二部分(如何列出命名元组的属性)。

要列出命名元组的属性,可以使用 _fields_asdict

>>> import collections as c
>>> Point = c.namedtuple("Point", ["x", "y"])
>>> p1 = Point(20, 15)
>>> print(p1._fields)
('x', 'y')
>>> print(p1._asdict())
{'x': 20, 'y': 15}
>>> class SubPoint(Point): pass
...
>>> p2 = SubPoint(20, 15)
>>> print(p2._fields)
('x', 'y')
>>> print(p2._asdict())
{'x': 20, 'y': 15}

注意,_fields 是在类上定义的,所以你也可以这样做:

>>> print(SubPoint._fields)
('x', 'y')

显然,_asdict 需要一个实例,这样它才能使用里面的值。

我用的是 Python 3.9.7 来做示例,我不太确定这些内容是什么时候添加的(也许知道的人可以评论一下)。

撰写回答