【问题标题】:Python: Extending a predefined named tuplePython:扩展预定义的命名元组
【发布时间】:2015-04-14 13:11:20
【问题描述】:

我有以下命名元组:

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 修改为列表,我将丢失类型信息,我该如何避免这种情况?

【问题讨论】:

    标签: python python-2.7 namedtuple


    【解决方案1】:

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

    这有点太多了。而不是子类,我只是重用源类型的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')
    

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

    【讨论】:

    • 这是文档建议的做法,但如果您有一个带有生成字段的自定义命名元组怎么办?
    • @Ethereal:所有 namedtuple 类都是自定义的。 _fields 属性仍将反映类上的实际字段。
    • 我发现这不是真的,_fields 不包含生成的字段!请参阅文档中的 hypot 示例。
    • @Ethereal 所以你说的是property 对象。我永远不会称其为生成的字段。不,namedtuple 对象的子类上的其他属性不会反映在_fields 属性中。如果您需要共享其他方法或属性,请考虑将它们放在一个混合类中,并在多个命名元组类之间重用它们。
    • @MartijnPieters 正确,对于命名混乱和建议感到抱歉。
    【解决方案2】:

    扩展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
    

    【讨论】:

      【解决方案3】:

      很容易将一些东西组合在一起,让您可以从其他命名元组组合命名元组以及引入新字段。

      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')
      

      【讨论】:

        猜你喜欢
        • 2019-04-23
        • 2020-11-20
        • 2021-07-08
        • 2011-03-26
        • 1970-01-01
        • 2015-10-31
        • 2021-04-29
        • 2011-03-06
        • 1970-01-01
        相关资源
        最近更新 更多