Pythonの「名前付きタプル」とは何ですか?


907

Python 3.1での変更を読んで、私は何かを見つけました...予期しない:

sys.version_infoタプルは名前付きタプルになりました

名前付きタプルについてこれまで聞いたことがなく、要素は(タプルやリストのように)番号で、または(dictsのように)キーでインデックス付けできると思いました。両方の方法でインデックス付けできるとは思いもしませんでした。

したがって、私の質問は次のとおりです。

  • 名前付きタプルとは何ですか?
  • それらを使用するには?
  • なぜ/いつ通常のタプルの代わりに名前付きタプルを使用する必要がありますか?
  • 名前付きタプルの代わりに通常のタプルを使用する必要があるのはなぜ/いつですか?
  • 「名前付きリスト」(名前付きタプルの可変バージョン)はありますか?

回答:


1196

名前付きタプルは、基本的に作成が簡単で軽量なオブジェクトタイプです。名前付きタプルインスタンスは、オブジェクトのような変数の逆参照または標準のタプル構文を使用して参照できます。それらstructは不変であることを除いて、他の一般的なレコードタイプと同様に使用できます。それらはPython 2.6とPython 3.0で追加されましたが、Python 2.4での実装方法はあります。

たとえば、ポイントをタプルとして表すのが一般的です(x, y)。これにより、次のようなコードが生成されます。

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)

名前付きタプルを使用すると、より読みやすくなります。

from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)

ただし、名前付きタプルは通常のタプルとの下位互換性があるため、以下は引き続き機能します。

Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
 # use tuple unpacking
x1, y1 = pt1

したがって、オブジェクト表記によってコードがよりPython的で読みやすくなると思われる場所では、タプルの代わりに名前付きタプルを使用する必要があります。私は個人的に、非常に単純な値の型を表すためにそれらを使用し始めました。特に、それらをパラメーターとして関数に渡す場合。タプルのパッキングのコンテキストを見なくても、関数が読みやすくなります。

さらに、関数を持たない通常の不変のクラスを置き換え、フィールドのみ置き換えることもできます。名前付きタプル型を基本クラスとして使用することもできます。

class Point(namedtuple('Point', 'x y')):
    [...]

ただし、タプルと同様に、名前付きタプルの属性は不変です。

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

値を変更できるようにするには、別のタイプが必要です。属性に新しい値を設定できる、変更可能なレコードタイプの便利なレシピがあります。

>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
    2.0

ただし、新しいフィールドを追加できる「名前付きリスト」の形式は知りません。この状況では、辞書を使用したいだけかもしれません。名前付きタプルはpt1._asdict()which を使用して辞書に変換でき{'x': 1.0, 'y': 5.0}、通常のすべての辞書関数で操作できます。

すでに述べたように、これらの例の元となった詳細については、ドキュメント確認する必要があります。


35
Python 3.7以降では、
データクラス

3
変更可能なレコードタイプが必要な場合-定義済みの単純なクラスを使用します__slots__
madzohan

データクラスではなくrcdtypeを使用する主な理由は何ですか
Voyager

dictの代わりに属性ディクショナリstackoverflow.com/questions/4984647/…–
mrgloom

これはいつもあなたが見つける答えなのでtyping.NamedTuple、型ヒントを可能にし、サブクラス化に特に便利なものも今ではあることを言及する価値があるかもしれません。
DerWeh、

101

namedtupleはタプルクラスを作成するためのファクトリ関数です。このクラスを使用すると、名前で呼び出すことができるタプルを作成することもできます。

import collections

#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"], verbose=False, rename=False)   

row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created

print row    #Prints: Row(a=1, b=2, c=3)
print row.a  #Prints: 1
print row[0] #Prints: 1

row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values

print row   #Prints: Row(a=2, b=3, c=4)

5
verboseパラメーターとrenameパラメーターはデフォルトでFalseに設定されているため、これらの値を明示的に設定する必要はありません。
Trismegistos 2014

namedtuple is a factory function for making a tuple class.これがおそらくここでの唯一の正しい答えです:P
Mr_and_Mrs_D '24年

90

名前付きタプルとは何ですか?

名前付きタプルはタプルです。

タプルができるすべてのことを行います。

しかし、それは単なるタプルではありません。

これは、名前付きフィールドと固定長を使用して、仕様に応じてプログラムで作成されるタプルの特定のサブクラスです。

たとえば、これはタプルのサブクラスを作成し、固定長(この場合は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

44

名前付きタプルは優れた機能であり、データの完全なコンテナです。データを「保存」する必要がある場合は、次のようなタプルまたは辞書を使用します。

user = dict(name="John", age=20)

または:

user = ("John", 20)

辞書のアプローチは、dictが変更可能でタプルよりも遅いため、圧倒的です。一方、タプルは不変で軽量ですが、データフィールドの多数のエントリを読み取ることはできません。

namedtuplesは、2つのアプローチの完全な妥協案です。優れた可読性、軽量性、および不変性を備えています(さらに、それらは多態的です!)。


9
名前でその属性にアクセスする場合namedtuplesが遅くdictsよりも方法であることに留意してください:ntuple.foontuple[1]後者はるかに高速です。もっとその上:stackoverflow.com/questions/2646157/...
Rotareti

28

名前付きタプルは、このようなバージョンをチェックするコードとの下位互換性を可能にします

>>> sys.version_info[0:2]
(3, 1)

この構文を使用することにより、将来のコードをより明示的にすることができます

>>> sys.version_info.major
3
>>> sys.version_info.minor
1

12

名前付きタプル

コードをクリーンアップして読みやすくする最も簡単な方法の1つです。タプルで何が起こっているかを自己文書化します。Namedtuplesインスタンスは、インスタンスごとのディクショナリがないため、通常のタプルと同じくらいメモリ効率がよく、ディクショナリよりも高速です。

from collections import namedtuple

Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])

 p = Color(170, 0.1, 0.6)
 if p.saturation >= 0.5:
     print "Whew, that is bright!"
 if p.luminosity >= 0.5:
     print "Wow, that is light"

タプルの各要素に名前を付けないと、次のようになります。

p = (170, 0.1, 0.6)
if p[1] >= 0.5:
    print "Whew, that is bright!"
if p[2]>= 0.5:
   print "Wow, that is light"

最初の例で何が起こっているのかを理解するのは非常に困難です。名前付きタプルでは、​​各フィールドに名前があります。そして、位置やインデックスではなく名前でアクセスします。の代わりにp[1]、p.saturationと呼ぶことができます。理解しやすいです。そして、それはよりきれいに見えます。

名前付きタプルのインスタンスを作成する方が、辞書を作成するよりも簡単です。

# dictionary
>>>p = dict(hue = 170, saturation = 0.1, luminosity = 0.6)
>>>p['hue']
170

#nametuple
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p.hue
170

あなたがnamedtupleを使うのはいつですか

  1. 今述べたように、namedtupleはタプルの理解をより簡単にします。したがって、タプル内のアイテムを参照する必要がある場合は、名前付きタプルとしてそれらを作成することは理にかなっています。
  2. 名前付きタプルは、辞書よりも軽量であるだけでなく、辞書とは異なり順序も保持します。
  3. 上記の例のように、namedtupleのインスタンスを作成する方が、dictionaryよりも簡単です。そして、名前付きタプルの項目を参照することは、辞書よりもきれいに見えます。p.hueというより p['hue']

構文

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
  • namedtupleはコレクションライブラリにあります。
  • typename:これは、新しいタプルサブクラスの名前です。
  • field_names:各フィールドの名前のシーケンス。リスト['x', 'y', 'z']または文字列x y z(コンマなし、空白のみ)またはのようなシーケンスにすることができますx, y, z
  • rename:renameがの場合True、無効なフィールド名は自動的に位置名に置き換えられます。たとえば、['abc', 'def', 'ghi','abc']はに変換され['abc', '_1', 'ghi', '_3']、キーワード'def'(関数を定義するための予約語であるため)と重複するフィールド名が削除され'abc'ます。
  • verbose:verboseがのTrue場合、クラス定義はビルドされる直前に出力されます。

必要に応じて、名前付きタプルにその位置でアクセスできます。p[1] == p.saturation。それでも通常のタプルのように解凍されます。

方法

すべての通常のタプルメソッドがサポートされています。例:min()、max()、len()、in、not in、concatenation(+)、index、sliceなど。namedtupleにはいくつか追加のものがあります。注:これらはすべてアンダースコアで始まります。_replace_make_asdict

_replace 指定されたフィールドを新しい値で置き換える名前付きタプルの新しいインスタンスを返します。

構文

somenamedtuple._replace(kwargs)

>>>from collections import namedtuple

>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)

>>>p._replace(hue=87)
Color(87, 0.1, 0.6)

>>>p._replace(hue=87, saturation=0.2)
Color(87, 0.2, 0.6)

注意:フィールド名は引用符で囲まれていません。ここではキーワードです。 覚えておいてください:タプルは不変です-それらがタプルと命名され、_replaceメソッドを持っている場合でも。_replace生成newインスタンスを、元の値を変更したり、古い値を置き換えたりすることはありません。もちろん、新しい結果を変数に保存できます。p = p._replace(hue=169)

_make

既存のシーケンスまたは反復可能から新しいインスタンスを作成します。

構文

somenamedtuple._make(iterable)

 >>>data = (170, 0.1, 0.6)
 >>>Color._make(data)
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make([170, 0.1, 0.6])  #the list is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make((170, 0.1, 0.6))  #the tuple is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make(170, 0.1, 0.6) 
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 15, in _make
TypeError: 'float' object is not callable

最後のものはどうなりましたか?括弧内の項目は反復可能でなければなりません。したがって、括弧内のリストまたはタプルは機能しますが、反復可能として囲まれていない一連の値はエラーを返します。

_asdict

フィールド名を対応する値にマップする新しいOrderedDictを返します。

構文

somenamedtuple._asdict()

 >>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])

リファレンスhttps : //www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

名前付きタプルに似ているが変更可能な名前付きリストもあり ますhttps://pypi.python.org/pypi/namedlist


ただし、PEP8に従って、単一の下線は独自の動作を持つ「弱い「内部使用」インジケーター」と見なされることに注意してください。_!で始まる関数を使用するときは注意してください。
イェンス

8

名前付きタプルとは何ですか?

名前が示すように、namedtupleは名前付きタプルです。標準のタプルでは、​​インデックスを使用して要素にアクセスしますが、namedtupleでは、要素の名前を定義できます。これは特にcsv(カンマ区切り値)ファイルを処理し、複雑で大きなデータセットを処理するのに非常に便利です。この場合、コードは(Pythonicではなく)インデックスを使用すると煩雑になります。

それらを使用するには?

>>>from collections import namedtuple
>>>saleRecord = namedtuple('saleRecord','shopId saleDate salesAmout totalCustomers')
>>>
>>>
>>>#Assign values to a named tuple 
>>>shop11=saleRecord(11,'2015-01-01',2300,150) 
>>>shop12=saleRecord(shopId=22,saleDate="2015-01-01",saleAmout=1512,totalCustomers=125)

読書

>>>#Reading as a namedtuple
>>>print("Shop Id =",shop12.shopId)
12
>>>print("Sale Date=",shop12.saleDate)
2015-01-01
>>>print("Sales Amount =",shop12.salesAmount)
1512
>>>print("Total Customers =",shop12.totalCustomers)
125

CSV処理の興味深いシナリオ:

from csv import reader
from collections import namedtuple

saleRecord = namedtuple('saleRecord','shopId saleDate totalSales totalCustomers')
fileHandle = open("salesRecord.csv","r")
csvFieldsList=csv.reader(fileHandle)
for fieldsList in csvFieldsList:
    shopRec = saleRecord._make(fieldsList)
    overAllSales += shopRec.totalSales;

print("Total Sales of The Retail Chain =",overAllSales)

5

Pythonの内部では、名前付きタプルと呼ばれるコンテナーが適切に使用されており、クラスの定義を作成するために使用でき、元のタプルのすべての機能を備えています。

名前付きタプルを使用すると、デフォルトのクラステンプレートに直接適用されて単純なクラスが生成されます。このメソッドを使用すると、多くのコードで読みやすくなり、クラスを定義するときにも非常に便利です。


2

名前付きタプルを使用する別の方法(新しい方法)は、パッケージの入力からNamedTupleを使用することです:namedtupleにヒントを入力します

この投稿のトップアンサーの例を使用して、それを使用する方法を見てみましょう。

(1)名前付きタプルを使用する前のコードは次のとおりです。

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
print(line_length)

(2)次に、名前付きタプルを使用します

from typing import NamedTuple, Number

NamedTupleクラスを継承し、新しいクラスで変数名を定義します。testはクラスの名前です。

class test(NamedTuple):
x: Number
y: Number

クラスからインスタンスを作成し、それらに値を割り当てます

pt1 = test(1.0, 5.0)   # x is 1.0, and y is 5.0. The order matters
pt2 = test(2.5, 1.5)

インスタンスの変数を使用して計算する

line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)
print(line_length)

1

これを試して:

collections.namedtuple()

基本的に、namedtuples作成が簡単な軽量オブジェクトタイプです。彼らはタプルを簡単なタスクのための便利なコンテナに変えます。ではnamedtuples、タプルのメンバーにアクセスするために整数インデックスを使用する必要はありません。

例:

コード1:

>>> from collections import namedtuple

>>> Point = namedtuple('Point','x,y')

>>> pt1 = Point(1,2)

>>> pt2 = Point(3,4)

>>> dot_product = ( pt1.x * pt2.x ) +( pt1.y * pt2.y )

>>> print dot_product
11

コード2:

>>> from collections import namedtuple

>>> Car = namedtuple('Car','Price Mileage Colour Class')

>>> xyz = Car(Price = 100000, Mileage = 30, Colour = 'Cyan', Class = 'Y')

>>> print xyz

Car(Price=100000, Mileage=30, Colour='Cyan', Class='Y')
>>> print xyz.Class
Y

-1

他の皆は既にそれに答えました、しかし私はまだ他に追加することがあると思います。

Namedtupleは、直感的にクラスを定義するショートカットと見なすことができます。

を定義する面倒で従来の方法を参照してくださいclass

class Duck:
    def __init__(self, color, weight):
        self.color = color
        self.weight = weight
red_duck = Duck('red', '10')

    In [50]: red_duck
    Out[50]: <__main__.Duck at 0x1068e4e10>
    In [51]: red_duck.color
    Out[51]: 'red'

はどうかと言うと namedtuple

from collections import namedtuple
Duck = namedtuple('Duck', ['color', 'weight'])
red_duck = Duck('red', '10')

In [54]: red_duck
Out[54]: Duck(color='red', weight='10')
In [55]: red_duck.color
Out[55]: 'red'

2
申し訳ありませんが、これは間違っています。名前付きタプルは、これらもサポートしています:red_duck[0]or len(red_duck)またはfor x in red_duck: print(x)。さらに、名前付きタプルは不変なので、これらの操作は失敗します:red_duck[0] = 2red_duck.foo = 'bar'。それらは不変なので、名前付きタプルをdictキーとして使用できます。
DenilsonSáMaia

はい、それは基本です。
微積分

1
@JawSawいいえ、それは「基本」ではありません。名前付きタプルは、通常のクラスとはまったく異なる機能セットをサポートしています。本質的に、名前付きタプルはクラスですが、クラスが名前付きタプルであることを意味するものではありません。
connectyourcharger
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.