Python:扩展预定义的命名元组

时间:2015-02-13 13:17:49

标签: python python-2.7 namedtuple

我有以下名为tuple:

from collections import namedtuple
ReadElement = namedtuple('ReadElement', 'address value')

然后我想要以下内容:

LookupElement = namedtuple('LookupElement', 'address value lookups')

两个命名元组之间存在重复,我如何将ReadElement子类化为包含其他字段?

class LookupElement(ReadElement):
    def __new__(self, address, value, lookups):
        self = super(LookupElement, self).__new__(address, value)
        l = list(self)
        l.append(lookups)
        return tuple(l)

然而,在 new 语句中创建了元组,如果我将self修改为列表,我将丢失类型信息,我该如何避免这种情况?

3 个答案:

答案 0 :(得分:27)

您可以创建namedtuple生成的类的子类,但是您需要更仔细地研究生成的类。您需要在额外字段中添加另一个__slots__属性,更新_fields属性,创建新的__repr___replace方法(他们对字段列表进行硬编码,类名)并为其他字段添加额外的property个对象。请参阅example in the documentation

这太过分了。我只是重用源类型的somenamedtuple._fields attribute而不是子类:

LookupElement = namedtuple('LookupElement', ReadElement._fields + ('lookups',))

field_names构造函数的namedtuple()参数不必是字符串,它也可以是字符串序列。只需取_fields并通过连接新元组添加更多元素。

演示:

>>> from collections import namedtuple
>>> ReadElement = namedtuple('ReadElement', 'address value')
>>> LookupElement = namedtuple('LookupElement', ReadElement._fields + ('lookups',))
>>> LookupElement._fields
('address', 'value', 'lookups')
>>> LookupElement('addr', 'val', 'lookup') 
LookupElement(address='addr', value='val', lookups='lookup')

这确实意味着扩展类型不是基类型的子类。如果你必须有一个类层次结构,那么我不是试图使命名元组适合该模型,而是转而使用dataclasses。在大多数名为元组的用例中,数据类可以起到相同的作用,但很容易被子类化。

答案 1 :(得分:1)

将某些内容组合在一起非常容易,它允许您从其他命名元组合组成命名元组以及引入新字段。

def extended_namedtuple(name, source_fields):
    assert isinstance(source_fields, list)
    new_type_fields = []
    for f in source_fields:
        try:
            new_type_fields.extend(f._fields)
        except:
            new_type_fields.append(f) 
    return namedtuple(name, new_type_fields) 

# source types
Name = namedtuple('Name', ['first_name', 'last_name'])
Address = namedtuple('Address', ['address_line1', 'city'])
# new type uses source types and adds additional ID field
Customer = extended_namedtuple('Customer', ['ID', Name, Address])
# using the new type
cust1 = Customer(1, 'Banana', 'Man', '29 Acacia Road', 'Nuttytown')
print(cust1)

这输出以下内容:

Customer(ID=1, first_name='Banana', last_name='Man', address_line1='29 Acacia Road', city='Nuttytown')

答案 2 :(得分:1)

扩展Martijn Pieters' answer:有一种 方式,可以使新的namedtuple类成为另一个的子类,而无需进行修改。只需单独创建新的namedtuple,然后使用其__new__方法而不是使用super

from collections import namedtuple

class ReadElement(namedtuple('ReadElement', ('address', 'value'))):
    def compute(self):
        return self.value + 1

_LookupElement = namedtuple('_LookupElement', ReadElement._fields + ('lookups',))

class LookupElement(_LookupElement, ReadElement):
    def __new__(self, address, value, lookups):
        return _LookupElement.__new__(LookupElement, address, value, lookups)

assert issubclass(LookupElement, ReadElement)
l = LookupElement('ad', 1, dict())
assert isinstance(l, ReadElement)
assert l.compute() == 2

这似乎也可以在不覆盖__new__的情况下起作用!

from collections import namedtuple

class ReadElement(namedtuple('ReadElement', ('address', 'value'))):
    def compute(self):
        return self.value + 1

class LookupElement(namedtuple('LookupElement', ReadElement._fields + ('lookups',)),
                    ReadElement):
    """nothing special to do"""
    pass