8.3. collections
--- 高性能なコンテナ・データ型¶
バージョン 2.4 で追加.
Source code: Lib/collections.py and Lib/_abcoll.py
このモジュールは、汎用の Python 組み込みコンテナ dict
, list
, set
, および tuple
に代わる、特殊なコンテナデータ型を実装しています。
名前付きフィールドを持つタプルのサブクラスを作成するファクトリ関数 |
バージョン 2.6 で追加. |
|
両端における append や pop を高速に行えるリスト風のコンテナ |
バージョン 2.4 で追加. |
|
ハッシュ可能なオブジェクトを数え上げる辞書のサブクラス |
バージョン 2.7 で追加. |
|
項目が追加された順序を記憶する辞書のサブクラス |
バージョン 2.7 で追加. |
|
ファクトリ関数を呼び出して存在しない値を供給する辞書のサブクラス |
バージョン 2.5 で追加. |
コンテナ型の作成に加えて、 collections モジュールは幾つかの ABC (abstract base classes = 抽象基底クラス) を提供しています。 ABC はクラスが特定のインタフェース持っているかどうか、たとえばハッシュ可能であるかやマッピングであるかを判定するのに利用します。
8.3.1. Counter
オブジェクト¶
便利で迅速な検数をサポートするカウンタツールが提供されています。例えば:
>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
... cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
-
class
collections.
Counter
([iterable-or-mapping])¶ Counter
はハッシュ可能なオブジェクトをカウントするdict
のサブクラスです。これは、要素を辞書のキーとして保存し、そのカウントを辞書の値として保存する、順序付けされていないコレクションです。カウントは、0 や負のカウントを含む整数値をとれます。Counter
クラスは、他の言語のバッグや多重集合のようなものです。要素は、 iterable から数え上げられたり、他の mapping (やカウンタ) から初期化されます:
>>> c = Counter() # a new, empty counter >>> c = Counter('gallahad') # a new counter from an iterable >>> c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping >>> c = Counter(cats=4, dogs=8) # a new counter from keyword args
カウンタオブジェクトは辞書のインタフェースを持ちますが、存在しない要素に対して
KeyError
を送出する代わりに 0 を返すという違いがあります:>>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # count of a missing element is zero 0
カウントを 0 に設定しても、要素はカウンタから取り除かれません。完全に取り除くには、
del
を使ってください:>>> c['sausage'] = 0 # counter entry with a zero count >>> del c['sausage'] # del actually removes the entry
バージョン 2.7 で追加.
カウンタオブジェクトは、すべての辞書に利用できるものに加え、 3 つのメソッドをサポートしています:
-
elements
()¶ それぞれの要素を、そのカウント分の回数だけ繰り返すイテレータを返します。要素は任意の順序で返されます。ある要素のカウントが 1 未満なら、
elements()
はそれを無視します。>>> c = Counter(a=4, b=2, c=0, d=-2) >>> list(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b']
-
most_common
([n])¶ 最も多い n 要素を、カウントが多いものから少ないものまで順に並べたリストを返します。 n が省略されるか
None
であれば、most_common()
はカウンタの すべての 要素を返します。等しいカウントの要素は任意に並べられます:>>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 2)]
-
subtract
([iterable-or-mapping])¶ 要素から iterable の要素または mapping の要素が引かれます。
dict.update()
に似ていますが、カウントを置き換えるのではなく引きます。入力も出力も、 0 や負になりえます。>>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.subtract(d) >>> c Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
普通の辞書のメソッドは、以下の 2 つのメソッドがカウンタに対して異なる振る舞いをするのを除き、
Counter
オブジェクトにも利用できます。-
update
([iterable-or-mapping])¶ 要素が iterable からカウントされるか、別の mapping (やカウンタ) が追加されます。
dict.update()
に似ていますが、カウントを置き換えるのではなく追加します。また、 iterable には(key, value)
対のシーケンスではなく、要素のシーケンスが求められます。
-
Counter
オブジェクトを使ったよくあるパターン:
sum(c.values()) # total of all counts
c.clear() # reset all counts
list(c) # list unique elements
set(c) # convert to a set
dict(c) # convert to a regular dictionary
c.items() # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1] # n least common elements
c += Counter() # remove zero and negative counts
Counter
オブジェクトを組み合わせて多重集合 (1 以上のカウントをもつカウンタ) を作るために、いくつかの数学演算が提供されています。足し算と引き算は、対応する要素を足したり引いたりすることによってカウンタを組み合わせます。共通部分と合併集合は、対応するカウントの最大値と最小値を返します。それぞれの演算はカウントに符号がついた入力を受け付けますが、カウントが 0 以下である結果は出力から除かれます。
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # add two counters together: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d # intersection: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
注釈
カウンタはもともと、推移するカウントを正の整数で表すために設計されました。しかし、他の型や負の値を必要とするユースケースを不必要に排除することがないように配慮されています。このようなユースケースの助けになるように、この節で最低限の範囲と型の制限について記述します。
Counter
クラス自体は辞書のサブクラスで、キーと値に制限はありません。値はカウントを表す数であることを意図していますが、値フィールドに任意のものを保存 できます 。most_common()
メソッドが要求するのは、値が順序付け可能なことだけです。c[key] += 1
のようなインプレース演算では、値の型に必要なのは足し算と引き算ができることだけです。よって分数、浮動小数点数、小数も使え、負の値がサポートされています。これと同じことが、負や 0 の値を入力と出力に許すupdate()
とsubtract()
メソッドにも言えます。多重集合メソッドは正の値を扱うユースケースに対してのみ設計されています。入力は負や 0 に出来ますが、正の値の出力のみが生成されます。型の制限はありませんが、値の型は足し算、引き算、比較をサポートしている必要があります。
elements()
メソッドは整数のカウントを要求します。これは 0 と負のカウントを無視します。
参考
Python 2.5 向けの Counter class と、それより前の Python 2.4 向けの Bag recipe 。
Smalltalk の Bag class 。
Wikipedia の Multisets の項目。
C++ multisets の例を交えたチュートリアル。
数学的な多重集合の演算とそのユースケースは、 Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19 を参照してください。
与えられた要素の集まりから与えられた大きさの別個の多重集合をすべて数え上げるには、
itertools.combinations_with_replacement()
を参照してください。map(Counter, combinations_with_replacement('ABC', 2)) --> AA AB AC BB BC CC
8.3.2. deque
オブジェクト¶
-
class
collections.
deque
([iterable[, maxlen]])¶ iterable で与えられるデータから、新しい deque オブジェクトを (
append()
をつかって) 左から右に初期化して返します。 iterable が指定されない場合、新しい deque オブジェクトは空になります。Deque とは、スタックとキューを一般化したものです (この名前は「デック」と発音され、これは「double-ended queue」の省略形です)。Deque はどちらの側からも append と pop が可能で、スレッドセーフでメモリ効率がよく、どちらの方向からもおよそ
O(1)
のパフォーマンスで実行できます。list
オブジェクトでも同様の操作を実現できますが、これは高速な固定長の操作に特化されており、内部のデータ表現形式のサイズと位置を両方変えるようなpop(0)
やinsert(0, v)
などの操作ではメモリ移動のためにO(n)
のコストを必要とします。バージョン 2.4 で追加.
maxlen が指定され無かったり
None``* だった場合、 deque は任意のサイズまで大きくなります。 そうでない場合、 deque のサイズは指定された最大長に制限されます。 長さが制限された deque がいっぱいになると、新しい要素を追加するときに追加した要素数分だけ追加したのと反対側から要素が捨てられます。 長さが制限された deque は Unix における ``tail
フィルタと似た機能を提供します。 トランザクションの tracking や最近使った要素だけを残したいデータプール (pool of data) などにも便利です。バージョン 2.6 で変更: maxlen パラメータを追加しました。
Deque オブジェクトは以下のようなメソッドをサポートしています:
-
append
(x)¶ x を deque の右側につけ加えます。
-
appendleft
(x)¶ x を deque の左側につけ加えます。
-
clear
()¶ deque からすべての要素を削除し、長さを 0 にします。
-
count
(x)¶ x に等しい deque の要素を数え上げます。
バージョン 2.7 で追加.
-
extend
(iterable)¶ イテレータ化可能な引数 iterable から得られる要素を deque の右側に追加し拡張します。
-
extendleft
(iterable)¶ イテレータ化可能な引数 iterable から得られる要素を deque の左側に追加し拡張します。注意: 左から追加した結果は、イテレータ引数の順序とは逆になります。
-
pop
()¶ deque の右側から要素をひとつ削除し、その要素を返します。要素がひとつも存在しない場合は
IndexError
を発生させます。
-
popleft
()¶ deque の左側から要素をひとつ削除し、その要素を返します。要素がひとつも存在しない場合は
IndexError
を発生させます。
-
remove
(value)¶ value の最初に現れるものを削除します。要素が見付からないない場合は
ValueError
を送出します。バージョン 2.5 で追加.
-
reverse
()¶ deque の要素をインプレースに逆転し、
None
を返します。バージョン 2.7 で追加.
-
rotate
(n=1)¶ deque の要素を全体で n ステップだけ右にローテートします。n が負の値の場合は、左にローテートします。
deque が空でないときは、 deque をひとつ右にローテートすることは
d.appendleft(d.pop())
と同じで、 deque をひとつ左にローテートすることはd.append(d.popleft())
と同じです。
deque オブジェクトは読み取り専用属性も 1 つ提供しています:
-
maxlen
¶ deque の最大長で、制限されていなければ
None
です。バージョン 2.7 で追加.
-
上記の操作のほかにも、deque は次のような操作をサポートしています: イテレータ化、pickle、 len(d)
, reversed(d)
, copy.copy(d)
, copy.deepcopy(d)
, in
演算子による包含検査、そして d[-1]
などの添え字による参照。両端についてインデックスアクセスは O(1) ですが、中央部分については O(n) の遅さです。高速なランダムアクセスが必要ならリストを使ってください。
例:
>>> from collections import deque
>>> d = deque('ghi') # make a new deque with three items
>>> for elem in d: # iterate over the deque's elements
... print elem.upper()
G
H
I
>>> d.append('j') # add a new entry to the right side
>>> d.appendleft('f') # add a new entry to the left side
>>> d # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])
>>> d.pop() # return and remove the rightmost item
'j'
>>> d.popleft() # return and remove the leftmost item
'f'
>>> list(d) # list the contents of the deque
['g', 'h', 'i']
>>> d[0] # peek at leftmost item
'g'
>>> d[-1] # peek at rightmost item
'i'
>>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d # search the deque
True
>>> d.extend('jkl') # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1) # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1) # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> deque(reversed(d)) # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear() # empty the deque
>>> d.pop() # cannot pop from an empty deque
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
d.pop()
IndexError: pop from an empty deque
>>> d.extendleft('abc') # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])
8.3.2.1. deque
のレシピ¶
この節では deque をつかったさまざまなアプローチを紹介します。
長さが制限された deque は Unix における tail
フィルタに相当する機能を提供します:
def tail(filename, n=10):
'Return the last n lines of a file'
return deque(open(filename), n)
別のアプローチとして deque を右に append して左に pop して使うことで追加した要素を維持するのに使えます:
def moving_average(iterable, n=3):
# moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
# http://en.wikipedia.org/wiki/Moving_average
it = iter(iterable)
d = deque(itertools.islice(it, n-1))
d.appendleft(0)
s = sum(d)
for elem in it:
s += elem - d.popleft()
d.append(elem)
yield s / float(n)
rotate()
メソッドのおかげで、 deque
の一部を切り出したり削除したりできることになります。たとえば del d[n]
の純粋な Python 実装では pop したい要素まで rotate()
します
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
deque
の切り出しを実装するのにも、同様のアプローチを使います。まず対象となる要素を rotate()
によって deque の左端までもってきてから、 popleft()
をつかって古い要素を消します。そして、 extend()
で新しい要素を追加したのち、逆のローテートでもとに戻せばよいのです。このアプローチをやや変えたものとして、Forth スタイルのスタック操作、つまり dup
, drop
, swap
, over
, pick
, rot
, および roll
を実装するのも簡単です。
8.3.3. defaultdict
オブジェクト¶
-
class
collections.
defaultdict
([default_factory[, ...]])¶ 新しいディクショナリ状のオブジェクトを返します。
defaultdict
は組込みのdict
のサブクラスです。メソッドをオーバーライドし、書き込み可能なインスタンス変数を1つ追加している以外はdict
クラスと同じです。同じ部分については以下では省略されています。1つめの引数は
default_factory
属性の初期値です。デフォルトはNone
です。残りの引数はキーワード引数もふくめ、dict
のコンストラクタにあたえられた場合と同様に扱われます。バージョン 2.5 で追加.
defaultdict
オブジェクトは標準のdict
に加えて、以下のメソッドを実装しています:-
__missing__
(key)¶ もし
default_factory
属性がNone
であれば、このメソッドはKeyError
例外を、 key を引数として発生させます。もし
default_factory
属性がNone
でなければ、このメソッドはdefault_factory
を引数なしで呼び出し、あたえられた key に対応するデフォルト値を作ります。そしてこの値を key に対応する値を辞書に登録して返ります。もし
default_factory
の呼出が例外を発生させた場合には、変更せずそのまま例外を投げます。このメソッドは
dict
クラスの__getitem__()
メソッドで、キーが存在しなかった場合によびだされます。値を返すか例外を発生させるのどちらにしても、__getitem__()
からもそのまま値が返るか例外が発生します。なお、
__missing__()
は__getitem__()
以外のいかなる演算に対しても呼び出され ません 。よってget()
は、普通の辞書と同様に、default_factory
を使うのではなくデフォルトとしてNone
を返します。
defaultdict
オブジェクトは以下のインスタンス変数をサポートしています:-
default_factory
¶ この属性は
__missing__()
メソッドによって使われます。これは存在すればコンストラクタの第1引数によって初期化され、そうでなければNone
になります。
-
8.3.3.1. defaultdict
の使用例¶
list
を default_factory
とすることで、キー=値ペアのシーケンスをリストの辞書へ簡単にグループ化できます。:
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
それぞれのキーが最初に登場したとき、マッピングにはまだ存在しません。そのためエントリは default_factory
関数が返す空の list
を使って自動的に作成されます。 list.append()
操作は新しいリストに紐付けられます。キーが再度出現下場合には、通常の参照動作が行われます(そのキーに対応するリストが返ります)。そして list.append()
操作で別の値をリストに追加します。このテクニックは dict.setdefault()
を使った等価なものよりシンプルで速いです:
>>> d = {}
>>> for k, v in s:
... d.setdefault(k, []).append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
default_factory
を int
にすると、 defaultdict
を(他の言語の bag や multisetのように)要素の数え上げに便利に使うことができます:
>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
... d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
最初に文字が出現したときは、マッピングが存在しないので default_factory
関数が int()
を呼んでデフォルトのカウント0を生成します。インクリメント操作が各文字を数え上げます。
常に0を返す int()
は特殊な関数でした。定数を生成するより速くて柔軟な方法は、 0に限らず何でも定数を生成する itertools.repeat()
を使うことです。
>>> def constant_factory(value):
... return itertools.repeat(value).next
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'
default_factory
を set
に設定することで、 defaultdict
をセットの辞書を作るために利用することができます:
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
... d[k].add(v)
...
>>> d.items()
[('blue', set([2, 4])), ('red', set([1, 3]))]
8.3.4. namedtuple()
名前付きフィールドを持つタプルのファクトリ関数¶
名前付きタプルはタプルの中の場所に意味を割り当てて、より読みやすく自己解説的なコードを書けるようにします。通常のタプルが利用されていた場所で利用でき、場所に対するインデックスの代わりに名前を使ってフィールドにアクセスできます。
-
collections.
namedtuple
(typename, field_names[, verbose=False][, rename=False])¶ typename という名前の tuple の新しいサブクラスを返します。新しいサブクラスは、 tuple に似ているけれどもインデックスやイテレータだけでなく属性名によるアクセスもできるオブジェクトを作るのに使います。このサブクラスのインスタンスは、わかりやすい docstring (型名と属性名が入っています) や、 tuple の内容を
name=value
という形のリストで返す使いやすい__repr__()
も持っています。field_names は
['x', 'y']
のような文字列のシーケンスです。 field_names には、代わりに各属性名を空白文字 (whitespace) および/またはカンマ (,) で区切った文字列を渡すこともできます。例えば、'x y'
や'x, y'
です。アンダースコア (_) で始まる名前を除いて、 Python の正しい識別子 (identifier) ならなんでも属性名として使うことができます。正しい識別子とはアルファベット(letters), 数字(digits), アンダースコア(_) を含みますが、数字やアンダースコアで始まる名前や、 class, for, return, global, pass, print, raise などといった
keyword
は使えません。rename が真なら、不適切なフィールド名は自動的に位置引数に置き換えられます。例えば
['abc', 'def', 'ghi', 'abc']
は、予約語def
と重複しているフィールド名abc
が除去され、['abc', '_1', 'ghi', '_3']
に変換されます。verbose が真なら、クラスを作る直前にクラス定義が表示されます。
名前付きタプルのインスタンスはインスタンスごとの辞書を持たないので、軽量で、普通のタプル以上のメモリを使用しません。
バージョン 2.6 で追加.
バージョン 2.7 で変更: rename のサポートを追加しました。
例:
>>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
class Point(tuple):
'Point(x, y)'
__slots__ = ()
_fields = ('x', 'y')
def __new__(_cls, x, y):
'Create new instance of Point(x, y)'
return _tuple.__new__(_cls, (x, y))
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Point object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 2:
raise TypeError('Expected 2 arguments, got %d' % len(result))
return result
def __repr__(self):
'Return a nicely formatted representation string'
return 'Point(x=%r, y=%r)' % self
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self))
def _replace(_self, **kwds):
'Return a new Point object replacing specified fields with new values'
result = _self._make(map(kwds.pop, ('x', 'y'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)
__dict__ = _property(_asdict)
def __getstate__(self):
'Exclude the OrderedDict from pickling'
pass
x = _property(_itemgetter(0), doc='Alias for field number 0')
y = _property(_itemgetter(1), doc='Alias for field number 1')
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessible by name
33
>>> p # readable __repr__ with a name=value style
Point(x=11, y=22)
名前付きタプルは csv
や sqlite3
モジュールが返すタプルのフィールドに名前を付けるときにとても便利です:
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
print emp.name, emp.title
import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
print emp.name, emp.title
タプルから継承したメソッドに加えて、名前付きタプルは3つの追加メソッドと一つの属性をサポートしています。フィールド名との衝突を避けるためにメソッド名と属性名はアンダースコアで始まります。
-
classmethod
somenamedtuple.
_make
(iterable)¶ 既存の sequence や Iterable から新しいインスタンスを作るクラスメソッド.
>>> t = [11, 22] >>> Point._make(t) Point(x=11, y=22)
-
somenamedtuple.
_asdict
()¶ フィールド名を対応する値にマッピングする新しい順序付き辞書 (
OrderedDict
) を返します:>>> p = Point(x=11, y=22) >>> p._asdict() OrderedDict([('x', 11), ('y', 22)])
バージョン 2.7 で変更: 通常の
dict
の代わりにOrderedDict
を返すようになりました。
-
somenamedtuple.
_replace
(**kwargs)¶ 指定されたフィールドを新しい値で置き換えた、新しい名前付きタプルを作って返します:
>>> p = Point(x=11, y=22) >>> p._replace(x=33) Point(x=33, y=22) >>> for partnum, record in inventory.items(): ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
-
somenamedtuple.
_fields
¶ フィールド名をリストにしたタプル. 内省 (introspection) したり、既存の名前付きタプルをもとに新しい名前つきタプルを作成する時に便利です。
>>> p._fields # view the field names ('x', 'y') >>> Color = namedtuple('Color', 'red green blue') >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) >>> Pixel(11, 22, 128, 255, 0) Pixel(x=11, y=22, red=128, green=255, blue=0)
文字列に格納された名前を使って名前つきタプルから値を取得するには getattr()
関数を使います:
>>> getattr(p, 'x')
11
辞書を名前付きタプルに変換するには、 **
演算子 (double-star-operator, 引数リストのアンパック で説明しています) を使います。:
>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)
名前付きタプルは通常の Python クラスなので、継承して機能を追加したり変更するのは容易です。次の例では計算済みフィールドと固定幅の print format を追加しています:
>>> class Point(namedtuple('Point', 'x y')):
... __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)
...
>>> for p in Point(3, 4), Point(14, 5/7.):
... print p
Point: x= 3.000 y= 4.000 hypot= 5.000
Point: x=14.000 y= 0.714 hypot=14.018
このサブクラスは __slots__
に空のタプルをセットしています。これにより、インスタンス辞書の作成を抑制してメモリ使用量を低く保つのに役立ちます。
サブクラス化は新しいフィールドを追加するのには適していません。代わりに、新しい名前付きタプルを _fields
属性を元に作成してください:
>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))
_replace()
でプロトタイプのインスタンスをカスタマイズする方法で、デフォルト値を実現できます:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
列挙型定数は名前付きタプルでも実装できますが、クラス定義を利用した方がシンプルで効率的です:
>>> Status = namedtuple('Status', 'open pending closed')._make(range(3))
>>> Status.open, Status.pending, Status.closed
(0, 1, 2)
>>> class Status:
... open, pending, closed = range(3)
参考
Python 2.4 向けの Named tuple recipe 。
8.3.5. OrderedDict
オブジェクト¶
順序付き辞書 (ordered dictionary) は、ちょうど普通の辞書と同じようなものですが、項目が挿入された順序を記憶します。順序付き辞書に渡ってイテレートするとき、項目はそのキーが最初に追加された順序で返されます。
-
class
collections.
OrderedDict
([items])¶ 通常の
dict
メソッドをサポートする、辞書のサブクラスのインスタンスを返します。 OrderedDict は、キーが最初に追加された順序を記憶します。新しい項目が既存の項目を上書きしても、元の挿入位置は変わらないままです。項目を削除して再挿入するとそれが最後に移動します。バージョン 2.7 で追加.
-
OrderedDict.
popitem
(last=True)¶ 順序付き辞書の
popitem()
メソッドは、(key, value) 対を返して消去します。この対は last が真なら後入先出で、偽なら先入先出で返されます。
通常のマッピングのメソッドに加え、順序付き辞書は reversed()
による逆順の反復もサポートしています。
OrderedDict
間の等価判定は順序が影響し、 list(od1.items())==list(od2.items())
のように実装されます。 OrderedDict
オブジェクトと他のマッピング (Mapping
) オブジェクトの等価判定は、順序に影響されず、通常の辞書と同様です。これによって、 OrderedDict
オブジェクトは通常の辞書が使われるところならどこでも使用できます。
OrderedDict
コンストラクタと update()
メソッドは、どちらもキーワード引数を受け付けますが、その順序は失われます。これは、Python の関数呼び出しの意味づけにおいて、キーワード引数は順序付けされていない辞書を用いて渡されるからです。
参考
Python 2.4 以降で動作する、 Equivalent OrderedDict recipe 。
8.3.5.1. OrderedDict
の例とレシピ¶
順序付き辞書は挿入順序を記憶するので、ソートと組み合わせて使うことで、ソートされた辞書を作れます:
>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])
この新しい順序付き辞書は、項目が削除されてもソートされた順序を保持します。しかし、キーが追加されるとき、そのキーは最後に追加され、ソートは保持されません。
キーが 最後に 挿入された順序を記憶するような、順序付き辞書の変種を作るのも簡単です。新しい項目が既存の項目を上書きしたら、元の挿入位置は最後に移動します:
class LastUpdatedOrderedDict(OrderedDict):
'Store items in the order the keys were last added'
def __setitem__(self, key, value):
if key in self:
del self[key]
OrderedDict.__setitem__(self, key, value)
順序付き辞書と Counter
クラスを組み合わせると、要素が最初に現れた順を記憶するカウンタができます:
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
8.3.6. コレクション抽象基底クラス¶
collections モジュールは以下の ABC (抽象基底クラス) を提供します:
ABC |
継承しているクラス |
抽象メソッド |
mixin メソッド |
---|---|---|---|
|
|||
|
|||
|
|||
|
|
||
|
|||
|
|||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|||
|
|||
|
|||
|
-
class
collections.
Container
¶ -
class
collections.
Hashable
¶ -
class
collections.
Sized
¶ -
class
collections.
Callable
¶ それぞれメソッド
__contains__()
,__hash__()
,__len__()
,__call__()
を提供するクラスの ABC です。
-
class
collections.
Iterable
¶ __iter__()
メソッドを提供するクラスの ABC です。 iterable の定義も参照してください。
-
class
collections.
Iterator
¶ __iter__()
メソッドとnext()
メソッドを提供するクラスの ABC です。 iterator の定義も参照してください。
-
class
collections.
MappingView
¶ -
class
collections.
ItemsView
¶ -
class
collections.
KeysView
¶ -
class
collections.
ValuesView
¶ マッピング、要素、キー、値の ビュー の ABC です。
これらの ABC はクラスやインスタンスが特定の機能を提供しているかどうかを調べるのに使えます。例えば:
size = None
if isinstance(myvar, collections.Sized):
size = len(myvar)
幾つかの ABC はコンテナ型 API を提供するクラスを開発するのを助ける mixin 型としても使えます。例えば、 Set
API を提供するクラスを作る場合、3つの基本になる抽象メソッド __contains__()
, __iter__()
, __len__()
だけが必要です。 ABC が残りの __and__()
や isdisjoint()
といったメソッドを提供します:
class ListBasedSet(collections.Set):
''' Alternate set implementation favoring space over speed
and not requiring the set elements to be hashable. '''
def __init__(self, iterable):
self.elements = lst = []
for value in iterable:
if value not in lst:
lst.append(value)
def __iter__(self):
return iter(self.elements)
def __contains__(self, value):
return value in self.elements
def __len__(self):
return len(self.elements)
s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2 # The __and__() method is supported automatically
Set
と MutableSet
を mixin 型として利用するときの注意点:
幾つかの set の操作は新しい set を作るので、デフォルトの mixin メソッドは iterable から新しいインスタンスを作成する方法を必要とします。クラスのコンストラクタは
ClassName(iterable)
の形のシグネチャを持つと仮定されます。内部の_from_iterable()
というクラスメソッドがcls(iterable)
を呼び出して新しい set を作る部分でこの仮定が使われています。コンストラクタのシグネチャが異なるクラスでSet
を使う場合は、 iterable 引数から新しいインスタンスを生成するように_from_iterable()
をオーバーライドする必要があります。(たぶん意味はそのままに速度を向上する目的で)比較をオーバーライドする場合、
__le__()
と__ge__()
だけを再定義すれば、その他の演算は自動的に追随します。Set
mixin型は set のハッシュ値を計算する_hash()
メソッドを提供しますが、すべての set が hashable や immutable とは限らないので、__hash__()
は提供しません。 mixin を使ってハッシュ可能な set を作る場合は、Set
とHashable
の両方を継承して、__hash__ = Set._hash
と定義してください。
参考
MutableSet
を使った例として OrderedSet recipe。