Python 如何在多重继承中使用namedtuples

Python 如何在多重继承中使用namedtuples,python,inheritance,multiple-inheritance,namedtuple,Python,Inheritance,Multiple Inheritance,Namedtuple,是否可以创建一个从多个实例继承的类,或创建具有相同效果的类(具有组合基类型字段的不可变类型)?我还没有找到这样做的方法 此示例说明了问题: >>> class Test(namedtuple('One', 'foo'), namedtuple('Two', 'bar')): >>> pass >>> t = Test(1, 2) TypeError: __new__() takes 2 positional arguments but

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

此示例说明了问题:

>>> 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中有多个名为tuple的
实例,那么它们必须共享一个基类
tuple的实例。要做到这一点,他们必须协调哪个
namedtuple
使用基元组中的哪个索引范围


有没有更简单的方法可以通过
命名为tuple
或类似的东西来实现多重继承?有人已经在某个地方实现了吗?

您可以使用装饰器或元类将父命名元组字段合并到一个新的命名元组中,并将其添加到类
\uuuu base\uuu

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
从集合导入namedtuple
def合并_字段(cls):
名称=cls.\u名称__
基数=cls.\uuu基数__
字段=[]
对于基数为c的情况:
如果不是hasattr(c,“_字段”):
持续
字段.扩展(如果f不在字段中,则f代表c中的f.\U字段)
如果len(字段)==0:
返回cls
组合的\u tuple=namedtuple(“%sCombinedNamedTuple”%name,字段)
返回类型(名称,(组合元组,)+base,dict(cls.\uuuuu dict)
类SomeParent(名为tuple('Two','bar')):
定义一些家长方法(自我):
返回“来自某个父对象的方法”
为其他父对象(对象)初始化:
定义初始值(自,*args,**kw):
print'从其他父级调用。_init__;with',args,kw
定义一些其他家长方法(自我):
返回“来自其他父对象的方法”
@合并字段
类测试(名为tuple('One','foo'),SomeParent,SomeOtherParent):
定义某些_方法(自身):
返回“对%s执行某些操作”%”(self)
打印测试__
# (
#   , 
#   , 
# )
t=Test(1,2)#从另一个父级调用。uuuu init_uuuuuu与(1,2){
打印t#测试(foo=1,bar=2)
print t t.some_method()#用Test做点什么(foo=1,bar=2)
从SomeParent打印t.some_parent_meth()#方法
从其他父对象打印t.some_other_parent_meth()#方法

好吧,如果您只想创建一个包含两个字段的namedtuple,那么很容易重新创建它:

One = namedtuple('One', 'foo')
Two = namedtuple('Two', 'bar')
Test = namedtuple('Test', One._fields+Two._fields)

这段代码采用了与Francis Colas类似的方法,尽管有点长:)

这是一个工厂函数,它接受任意数量的父namedtuple,并创建一个新的namedtuple,按顺序包含父级中的所有字段,跳过任何重复的字段名

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)

如果超类包含不同数量的字段或不同的字段名,您将如何解决歧义?我不理解这个问题。我希望所有的行为都与常规可变类一样,只是它是不可变的。子类将具有所有基类的所有字段。因此,上面的例子相当于
namedtuple(Test,('foo','bar'))
。问题是,由于您没有定义
Test.\uuuu init\uuuuuu
,因此只调用第一个基类的
\uu init\uuuuu
,并且该函数只接受一个(附加)参数。这对于我给出的示例是有效的,但实际情况稍微复杂一些。在我的实际用例中,我有一个直接或间接从
namedtuple
继承的类。显然我把我的例子简化了。好吧,你能重写一个不那么简单的例子吗?因为我不知道在这种情况下,组合是否不是一种更好的方法。