Python: 扩展一个预定义的命名元组
Python: Extending a predefined named 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 修改为列表,我将丢失类型信息,如何避免这种情况?
您可以子class一个namedtuple
生成的class,但您需要更仔细地研究生成的class。您需要添加另一个带有额外字段的 __slots__
属性,更新 _fields
属性,创建新的 __repr__
和 _replace
方法(它们硬编码字段列表和 class 名称)并为附加字段添加额外的 property
对象。见 example in the documentation.
这些工作有点太多了。而不是 subclass,我只是重用源类型的 somenamedtuple._fields
attribute:
LookupElement = namedtuple('LookupElement', ReadElement._fields + ('lookups',))
namedtuple()
构造函数的 field_names
参数不一定是字符串,也可以是字符串序列。只需采用 _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')
这意味着扩展类型不是基本类型的子class。如果您必须有一个 class 层次结构,那么我不会尝试使命名元组适合该模型,而是改用 。 Dataclasses 在大多数使用命名元组的用例中可以达到相同的目的,但可以很容易地被 subclassed.
很容易将一些东西组合在一起,使您可以从其他命名元组组成命名元组并引入新字段。
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')
扩展:是一种使新命名元组class成为另一个的子class的方法,而不必破解。只需单独创建新的 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
我有以下命名元组:
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 修改为列表,我将丢失类型信息,如何避免这种情况?
您可以子class一个namedtuple
生成的class,但您需要更仔细地研究生成的class。您需要添加另一个带有额外字段的 __slots__
属性,更新 _fields
属性,创建新的 __repr__
和 _replace
方法(它们硬编码字段列表和 class 名称)并为附加字段添加额外的 property
对象。见 example in the documentation.
这些工作有点太多了。而不是 subclass,我只是重用源类型的 somenamedtuple._fields
attribute:
LookupElement = namedtuple('LookupElement', ReadElement._fields + ('lookups',))
namedtuple()
构造函数的 field_names
参数不一定是字符串,也可以是字符串序列。只需采用 _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')
这意味着扩展类型不是基本类型的子class。如果您必须有一个 class 层次结构,那么我不会尝试使命名元组适合该模型,而是改用
很容易将一些东西组合在一起,使您可以从其他命名元组组成命名元组并引入新字段。
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')
扩展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