名前付きタプルとは何ですか?
名前付きタプルはタプルです。
タプルができるすべてのことを行います。
しかし、それは単なるタプルではありません。
これは、名前付きフィールドと固定長を使用して、仕様に応じてプログラムで作成されるタプルの特定のサブクラスです。
たとえば、これはタプルのサブクラスを作成し、固定長(この場合は3つ)であるだけでなく、タプルが使用されるすべての場所で中断せずに使用できます。これはリスコフ代替性として知られています。
Python 3.6の新機能で、クラス定義typing.NamedTuple
を使用して名前付きタプルを作成できます。
from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
上記は、型注釈とdocstringが追加されていることを除いて、以下と同じです。以下はPython 2+で利用可能です:
>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)
これはそれをインスタンス化します:
>>> ant = ANamedTuple(1, 'bar', [])
これを検査して、その属性を使用できます。
>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']
より深い説明
名前付きタプルを理解するには、最初にタプルとは何かを知る必要があります。タプルは基本的に不変(メモリ内で変更できません)のリストです。
通常のタプルを使用する方法は次のとおりです。
>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'
反復可能なアンパックでタプルを拡張できます:
>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
名前付きタプルは、要素にインデックスだけでなく名前でアクセスできるタプルです。
次のような名前付きタプルを作成します。
>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])
スペースで区切られた名前の単一の文字列を使用することもできます。これは、APIを少し読みやすくします。
>>> Student = namedtuple('Student', 'first last grade')
それらを使用するには?
あなたはタプルができるすべてのことをすることができ(上記を参照)、次のこともできます:
>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')
コメント者は尋ねました:
大規模なスクリプトまたはプログラムでは、通常、名前付きタプルをどこで定義しますか?
作成するタイプnamedtuple
は、基本的には簡単な速記で作成できるクラスです。クラスのように扱います。それらをモジュールレベルで定義して、pickleや他のユーザーが見つけられるようにします。
グローバルモジュールレベルでの動作例:
>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')
そして、これは定義の検索に失敗したことを示しています。
>>> def foo():
... LocalNT = namedtuple('LocalNT', 'foo bar')
... return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed
なぜ/いつ通常のタプルの代わりに名前付きタプルを使用する必要がありますか?
コードを改善してタプル要素のセマンティクスをコードで表現する場合に使用します。
データ属性が変更されず、機能のないオブジェクトを使用する場合は、オブジェクトの代わりに使用できます。
これらをサブクラス化して機能を追加することもできます。次に例を示します。
class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
名前付きタプルの代わりに通常のタプルを使用する必要があるのはなぜ/いつですか?
名前付きタプルの使用からタプルへの切り替えは、おそらく退行です。事前の設計決定は、タプルを使用した場合に含まれる追加のコードのコストが読みやすさの向上に見合うかどうかに集中します。
名前付きタプルとタプルで使用される余分なメモリはありません。
「名前付きリスト」(名前付きタプルの可変バージョン)はありますか?
静的サイズのリストのすべての機能を実装するスロット付きオブジェクト、または名前付きタプルのように機能するサブクラス化リストのいずれかを探しています(これにより、リストのサイズの変更がブロックされます)。
最初の例として、現在は拡張されており、おそらくLiskovの置換も可能です。
from collections import Sequence
class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""
__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
そして使用するには、サブクラスを作成して定義するだけ__slots__
です:
class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize
>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A