是否可以创建一个继承自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
或类似的多重继承?有人已经在某处实现了吗?
答案 0 :(得分:5)
您可以使用装饰器或元类将父命名元组字段组合成一个新的命名元组,并将其添加到类__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
答案 1 :(得分:4)
这段代码采用了与Francis Colas相似的方法,虽然它有点长了:)
这是一个工厂函数,它接受任意数量的父命名元组,并创建一个新的命名元组,它包含父项中的所有字段,按顺序跳过任何重复的字段名称。
from collections import namedtuple
def combined_namedtuple(typename, *parents):
#Gather fields, in order, from parents, skipping dupes
fields = []
for t in parents:
for f in t._fields:
if f not in fields:
fields.append(f)
return namedtuple(typename, fields)
nt1 = namedtuple('One', ['foo', 'qux'])
nt2 = namedtuple('Two', ['bar', 'baz'])
Combo = combined_namedtuple('Combo', nt1, nt2)
ct = Combo(1, 2, 3, 4)
print ct
<强>输出强>
Combo(foo=1, qux=2, bar=3, baz=4)
答案 2 :(得分:3)
好吧,如果您只想要一个包含两个字段的命名元组,那么很容易重新创建它:
One = namedtuple('One', 'foo')
Two = namedtuple('Two', 'bar')
Test = namedtuple('Test', One._fields+Two._fields)