タプルとは何ですか?
名前付きタプルはタプルです。
タプルが実行できるすべてのことを実行します。
しかし、それは単なるタプルではありません。
これは、指定されたフィールドと固定長を使用して、仕様に合わせてプログラムで作成されたタプルの特定のサブクラスです。
これは、たとえば、タプルのサブクラスを作成し、固定長(この場合は3つ)である以外に、タプルが壊れることなく使用されるすべての場所で使用できます。これは、リスコフの置換可能性として知られています。
Python 3.6の新機能として、クラス定義を使用しtyping.NamedTuple
て名前付きタプルを作成できます。
from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
上記は以下と同じですが、上記に型注釈とdocstringが追加されている点が異なります。以下はPython2+で利用できます。
>>> 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)
名前付きタプルの代わりに通常のタプルを使用する必要があるのはなぜ/いつですか?
名前付きタプルの使用からタプルへの切り替えはおそらくリグレッションになります。事前の設計上の決定は、関連する追加のコードからのコストが、タプルが使用されるときに読みやすさを向上させる価値があるかどうかに集中します。
名前付きタプルとタプルで使用される余分なメモリはありません。
「名前付きリスト」(名前付きタプルの変更可能なバージョン)はありますか?
静的なサイズのリストのすべての機能を実装するスロットオブジェクト、または名前付きタプルのように機能する(そして、リストのサイズの変更を何らかの方法でブロックする)サブクラス化されたリストのいずれかを探しています。
現在拡張されており、おそらくリスコフの置換可能でさえ、最初の例です。
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