How to use namedtuples in multiple inheritance

喜欢而已 提交于 2019-11-29 12:46:42

You could use a decorator or metaclass to combined the parent named tuple fields into a new named tuple and add it to the class __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

This code adopts a similar approach to Francis Colas', although it's somewhat longer :)

It's a factory function that takes any number of parent namedtuples, and creates a new namedtuple that has all the fields in the parents, in order, skipping any duplicate field names.

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

output

Combo(foo=1, qux=2, bar=3, baz=4)

Well, if you just want a namedtuple with both the fields, it's easy to just recreate it:

One = namedtuple('One', 'foo')
Two = namedtuple('Two', 'bar')
Test = namedtuple('Test', One._fields+Two._fields)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!