web-dev-qa-db-ja.com

Python:事前定義された名前付きタプルの拡張

次の名前のタプルがあります。

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

そして、私は以下が欲しい:

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

2つの名前付きタプル間に重複がありますが、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ステートメントで作成されます。自分をリストに変更してタイプ情報を失うと、どうすればこれを回避できますか?

21
Har

namedtupleで生成されたクラスをサブクラス化することはできますが、生成されたクラスをさらに詳しく調べる必要があります。別の___slots___属性を追加のフィールドで追加し、__fields_属性を更新し、新しい___repr___および__replace_メソッドを作成する必要があります(フィールドリストとクラス名をハードコードします) )、追加のフィールドに追加のpropertyオブジェクトを追加します。ドキュメントの の例 を参照してください。

それはすべて少しやり過ぎです。サブクラスではなく、ソースタイプの _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 代わりに。データクラスは、タプルが使用されるほとんどのユースケースで同じ目的を果たしますが、簡単にサブクラス化できます。

32
Martijn Pieters

他の名前付きタプルから名前付きタプルを作成したり、新しいフィールドを導入したりすることができるものを組み合わせるのは非常に簡単です。

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
samaspin