Skip to main content
 首页 » 编程设计

python之如何在多重继承中使用命名元组

2025年05月04日8bjzhanghao

是否可以创建一个继承自 namedtuple 的多个实例的类? ,或创建具有相同效果的东西(具有结合了基本类型字段的不可变类型)?我还没有找到这样做的方法。

这个例子说明了问题:

>>> class Test(namedtuple('One', 'foo'), namedtuple('Two', 'bar')): 
>>>    pass 
 
>>> t = Test(1, 2) 
TypeError: __new__() takes 2 positional arguments but 3 were given 
 
>>> t = Test(1) 
>>> t.foo 
1 
>>> t.bar 
1 

问题似乎是 namedtuple 没有使用 super 来初始化它的基类,在创建一个时可以看到:

>>> namedtuple('Test', ('field'), verbose=True) 
[...]     
class Test(tuple): 
[...] 
    def __new__(_cls, field,): 
        'Create new instance of Test(field,)' 
        return _tuple.__new__(_cls, (field,)) 

即使我考虑编写自己的 namedtuple 版本来解决这个问题,也不清楚如何去做。如果一个类的 MRO 中有多个 namedtuple 实例,则它们必须共享基类 tuple 的单个实例。为此,他们必须协调哪个 namedtuple 使用基元组中的哪个索引范围。

有没有更简单的方法来使用 namedtuple 或类似的东西实现多重继承?有人已经在某处实现了吗?

请您参考如下方法:

您可以使用装饰器或元类将父命名元组字段组合成一个新的命名元组并将其添加到类 __bases__:

from collections import namedtuple 
 
def merge_fields(cls): 
    name = cls.__name__ 
    bases = cls.__bases__ 
 
    fields = [] 
    for c in bases: 
        if not hasattr(c, '_fields'): 
            continue 
        fields.extend(f for f in c._fields if f not in fields) 
 
    if len(fields) == 0: 
        return cls 
 
    combined_tuple = namedtuple('%sCombinedNamedTuple' % name, fields) 
    return type(name, (combined_tuple,) + bases, dict(cls.__dict__)) 
 
 
class SomeParent(namedtuple('Two', 'bar')): 
 
    def some_parent_meth(self): 
        return 'method from SomeParent' 
 
 
class SomeOtherParent(object): 
 
    def __init__(self, *args, **kw): 
        print 'called from SomeOtherParent.__init__ with', args, kw 
 
    def some_other_parent_meth(self): 
        return 'method from SomeOtherParent' 
 
 
@merge_fields 
class Test(namedtuple('One', 'foo'), SomeParent, SomeOtherParent): 
 
    def some_method(self): 
        return 'do something with %s' % (self,) 
 
 
print Test.__bases__ 
# ( 
#   <class '__main__.TestCombinedNamedTuple'>, <class '__main__.One'>,  
#   <class '__main__.SomeParent'>, <class '__main__.SomeOtherParent'> 
# ) 
t = Test(1, 2)  # called from SomeOtherParent.__init__ with (1, 2) {}  
print t  # Test(foo=1, bar=2) 
print t.some_method()  # do something with Test(foo=1, bar=2) 
print t.some_parent_meth()  # method from SomeParent 
print t.some_other_parent_meth()  # method from SomeOtherParent