Python Language
リスト内包
サーチ…
前書き
Pythonのリスト内包は、簡潔で構文的な構造です。それらは、リスト内の各要素に関数を適用することによって、他のリストからリストを生成するために利用することができます。次のセクションでは、これらの式の使用方法を説明し、説明します。
構文
- [x、(1、2、3)] xのリスト理解、[2、3、4]
- (1、2、3)#のジェネレータ式のxのx + 1は2、3、4
- [xが(1,2,3)の場合はx%2 == 0の場合はx]フィルタを使用したリストの理解、[2]
- [x + 1ならx + 1 == 0、xは(1、2、3)でxの場合]#リスト内包
- [xが0の場合はx + 1、xが0の場合は範囲(-3,4)のxはxです。]#ターナリとフィルタリングによるリストの理解
- {xは(1、2、2、3)でxのために}#は理解を設定し、{1、2、3}
- {'a':1、 'b':2}(Python 2.7+および{b '、2)}を与える。 3.0+のみ)
- [10,20]中のyの[1、2]中のxのためのx + y]ネストされたループ[11,21,12,22]
- [3、4、5]のyのx> 2の場合、[1、2、3のxのxの場合]#ループの1番目の条件のチェック
- x> 2の場合、[3、4、5]のyの[1、2、3のxのyのxの値]#2番目のループの条件のチェック
- [xがx%2の場合xrange x(x)== 0]#ループされた数値が奇数の場合にチェックされる条件
備考
内包表記は、特定の言語固有のデータ構造や表現を定義する構文構造です。理解の適切な使用は、これらを容易に理解される表現に再解釈する。式として、次のように使用できます。
- 課題の右側に
- 関数呼び出しの引数として
- ラムダ関数の本体で
- スタンドアロンのステートメントとして。 (例:
[print(x) for x in range(10)]
)
リストの理解
リストの理解は、 反復可能な各要素に式を適用することによって新しいlist
作成します。最も基本的な形式は次のとおりです。
[ <expression> for <element> in <iterable> ]
オプションのif条件もあります:
[ <expression> for <element> in <iterable> if <condition> ]
<iterable>
<element>
内の各<element>
は、(オプションの) <condition>
がtrueと評価された場合に<expression>
プラグインされます 。すべての結果は新しいリストにまとめて返されます。 ジェネレータ式は遅れて評価されますが、リスト内包表記はイテレータの長さに比例してメモリを即座に消費するイテレータ全体を評価します。
二乗された整数のlist
を作成するには:
squares = [x * x for x in (1, 2, 3, 4)]
# squares: [1, 4, 9, 16]
for
式は、 x
を(1, 2, 3, 4)
から順に各値に設定します。式x * x
の結果は、内部list
追加されlist
。完了時に内部list
が変数のsquares
に割り当てられます。
( ここで説明するように ) 速度の向上のほかに、リストの理解は、以下のforループとほぼ同じです。
squares = []
for x in (1, 2, 3, 4):
squares.append(x * x)
# squares: [1, 4, 9, 16]
各要素に適用される式は、必要に応じて複雑にすることができます。
# Get a list of uppercase characters from a string
[s.upper() for s in "Hello World"]
# ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']
# Strip off any commas from the end of strings in a list
[w.strip(',') for w in ['these,', 'words,,', 'mostly', 'have,commas,']]
# ['these', 'words', 'mostly', 'have,commas']
# Organize letters in words more reasonably - in an alphabetical order
sentence = "Beautiful is better than ugly"
["".join(sorted(word, key = lambda x: x.lower())) for word in sentence.split()]
# ['aBefiltuu', 'is', 'beertt', 'ahnt', 'gluy']
else
else
はList comprehension構造体で使用できますが、構文に関しては注意してください。 if / else節は、 for
ループの前for
使用する必要があります。
# create a list of characters in apple, replacing non vowels with '*'
# Ex - 'apple' --> ['a', '*', '*', '*' ,'e']
[x for x in 'apple' if x in 'aeiou' else '*']
#SyntaxError: invalid syntax
# When using if/else together use them before the loop
[x if x in 'aeiou' else '*' for x in 'apple']
#['a', '*', '*', '*', 'e']
これは、別の言語構造、 条件式を使用していることに注意してください。それ自体は理解構文の一部ではありません。一方、 if
後for…in
リスト内包の一部であり 、反復可能なソースから要素をフィルタリングするために使用されます。
二重反復
二重反復の順序[... for x in ... for y in ...]
は、自然な、または反直観的です。経験則は、同等のfor
ループに従うことです。
def foo(i):
return i, i + 0.5
for i in range(3):
for x in foo(i):
yield str(x)
これは次のようになる:
[str(x)
for i in range(3)
for x in foo(i)
]
これは[str(x) for i in range(3) for x in foo(i)]
として1行に圧縮することができます[str(x) for i in range(3) for x in foo(i)]
インプレース突然変異およびその他の副作用
リストの理解を使用する前に、通常はNone
返す副作用( mutatingまたはin-place関数)と呼ばれる関数と興味深い値を返す関数の違いを理解してください。
多くの関数(特に純粋な関数)は、単にオブジェクトを取得してオブジェクトを返すだけです。 インプレイス関数は、既存のオブジェクトを変更します。これは副作用と呼ばれます 。他の例には、印刷などの入力および出力操作が含まれる。
list.sort()
、リストをインプレースでソートし(元のリストを変更することを意味します)、値None
を返します。したがって、それはリストの理解の中で期待どおりに機能しません:
[x.sort() for x in [[2, 1], [4, 3], [0, 1]]]
# [None, None, None]
代わりに、 sorted()
は、インプレースをソートするのではなくソートされたlist
返します。
[sorted(x) for x in [[2, 1], [4, 3], [0, 1]]]
# [[1, 2], [3, 4], [0, 1]]
I / Oやインプレース機能などの副作用のための理解を使用することが可能です。しかし、forループは通常より読みやすくなります。これはPython 3で動作しますが、
[print(x) for x in (1, 2, 3)]
代わりに以下を使用します。
for x in (1, 2, 3):
print(x)
場合によっては、副作用機能がリストの理解に適しています。 random.randrange()
は、乱数ジェネレータの状態を変更する副作用がありますが、興味深い値も返します。さらに、 next()
はイテレータで呼び出すことができます。
次の乱数生成器は純粋ではありませんが、式が評価されるたびに乱数生成器がリセットされるので意味があります:
from random import randrange
[randrange(1, 7) for _ in range(10)]
# [2, 3, 2, 1, 1, 5, 2, 4, 3, 5]
リスト内包表記の空白
より複雑なリスト内包表記は、望ましくない長さに達するか、または読みにくくなる可能性があります。例ではあまり一般的ではありませんが、リスト内包を複数の行に分割することは可能です:
[
x for x
in 'foo'
if x not in 'bar'
]
辞書の理解
辞書の理解は、リストの代わりに辞書オブジェクトを生成することを除いて、リストの理解に似ています。
基本的な例:
{x: x * x for x in (1, 2, 3, 4)}
# Out: {1: 1, 2: 4, 3: 9, 4: 16}
これはちょうど別の書き込み方法です。
dict((x, x * x) for x in (1, 2, 3, 4))
# Out: {1: 1, 2: 4, 3: 9, 4: 16}
リストの理解と同様に、私たちはいくつかの基準を満たすdict要素だけを生成するために、dictの理解の中で条件文を使うことができます。
{name: len(name) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6}
# Out: {'Exchange': 8, 'Overflow': 8}
または、ジェネレータ式を使用して書き直します。
dict((name, len(name)) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6)
# Out: {'Exchange': 8, 'Overflow': 8}
辞書から始まり、辞書理解をキーと値のペアのフィルターとして使用する
initial_dict = {'x': 1, 'y': 2}
{key: value for key, value in initial_dict.items() if key == 'x'}
# Out: {'x': 1}
キーと辞書の値の切り替え(逆辞書)
単純なハッシュ可能な値を含むdictを持っている場合(重複した値は予期しない結果を招く可能性があります)
my_dict = {1: 'a', 2: 'b', 3: 'c'}
キーと値を入れ替えるには、コーディングスタイルに応じていくつかの方法があります。
-
swapped = {v: k for k, v in my_dict.items()}
-
swapped = dict((v, k) for k, v in my_dict.iteritems())
-
swapped = dict(zip(my_dict.values(), my_dict))
-
swapped = dict(zip(my_dict.values(), my_dict.keys()))
-
swapped = dict(map(reversed, my_dict.items()))
print(swapped)
# Out: {a: 1, b: 2, c: 3}
辞書をマージする
辞書を組み合わせ、必要に応じて入れ子になった辞書の理解度を使って古い値を上書きします。
dict1 = {'w': 1, 'x': 1}
dict2 = {'x': 2, 'y': 2, 'z': 2}
{k: v for d in [dict1, dict2] for k, v in d.items()}
# Out: {'w': 1, 'x': 2, 'y': 2, 'z': 2}
ただし、辞書のアンパック( PEP 448 )が優先される場合があります。
{**dict1, **dict2}
# Out: {'w': 1, 'x': 2, 'y': 2, 'z': 2}
注意 : 辞書内包表記は 2.0で追加されたリストの内包表記とは異なり、Python 3.0で追加され、2.7以降にバックポートされました。バージョン2.7では、ジェネレータ式とdict()
組み込み関数を使用して、辞書内包表記の動作をシミュレートできます。
ジェネレータの式
ジェネレータの式はリスト内包表記と非常によく似ています。主な違いは、一度に完全な結果セットを作成しないことです。生成されたジェネレータオブジェクトは繰り返し処理されます。
たとえば、次のコードの違いを参照してください。
# list comprehension
[x**2 for x in range(10)]
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# generator comprehension
(x**2 for x in xrange(10))
# Output: <generator object <genexpr> at 0x11b4b7c80>
これらは2つの非常に異なるオブジェクトです。
リストの理解は
list
オブジェクトを返しますが、ジェネレータの理解はgenerator
返します。generator
オブジェクトは索引付けできず、next
関数を使用して項目を順番に取得します。
注 : xrange
もジェネレータオブジェクトを作成するので、 xrange
を使用します。 rangeを使用すると、リストが作成されます。また、 xrange
はpython 2のそれ以降のバージョンにのみ存在しますxrange
3では、 range
はジェネレータを返します。詳細については、 range関数とxrange関数の違いの例を参照してください。
g = (x**2 for x in xrange(10))
print(g[0])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'generator' object has no attribute '__getitem__'
g.next() # 0
g.next() # 1
g.next() # 4
...
g.next() # 81
g.next() # Throws StopIteration Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
注:
Iterator.next()
とxrange()
はPython 3には存在しないので、関数g.next()
はnext(g)
とxrange
Iterator.next()
range
next(g)
でxrange
てください。
これらの両方を同様の方法で反復することはできますが、
for i in [x**2 for x in range(10)]:
print(i)
"""
Out:
0
1
4
...
81
"""
for i in (x**2 for x in xrange(10)):
print(i)
"""
Out:
0
1
4
.
.
.
81
"""
ユースケース
ジェネレータの式は遅延評価されます。つまり、ジェネレータが反復されたときにのみ各値を生成して返します。これは、大量のデータセットを反復処理して、メモリ内のデータセットの複製を作成する必要性を避ける場合に、しばしば役に立ちます。
for square in (x**2 for x in range(1000000)):
#do something
別の一般的な使用例は、そうする必要がなければ、反復可能なもの全体を反復することを避けることです。この例では、項目はget_objects()
繰り返しごとにリモートAPIから取得されます。何千ものオブジェクトが存在し、1つずつ検索されなければならず、パターンに一致するオブジェクトが存在するかどうかだけを知る必要があります。ジェネレータ式を使用すると、パターンに一致するオブジェクトが発生したとき。
def get_objects():
"""Gets objects from an API one by one"""
while True:
yield get_next_item()
def object_matches_pattern(obj):
# perform potentially complex calculation
return matches_pattern
def right_item_exists():
items = (object_matched_pattern(each) for each in get_objects())
for item in items:
if item.is_the_right_one:
return True
return False
理解を設定する
セットの理解はに似ているリストと辞書の理解が、それは生産セットユニークな要素の順序なしコレクションです。
# A set containing every value in range(5):
{x for x in range(5)}
# Out: {0, 1, 2, 3, 4}
# A set of even numbers between 1 and 10:
{x for x in range(1, 11) if x % 2 == 0}
# Out: {2, 4, 6, 8, 10}
# Unique alphabetic characters in a string of text:
text = "When in the Course of human events it becomes necessary for one people..."
{ch.lower() for ch in text if ch.isalpha()}
# Out: set(['a', 'c', 'b', 'e', 'f', 'i', 'h', 'm', 'l', 'o',
# 'n', 'p', 's', 'r', 'u', 't', 'w', 'v', 'y'])
セットは順序付けされていないことに注意してください。これは、セット内の結果の順序が上の例で示されたものと異なる可能性があることを意味します。
注 :set comprehensionは、2.0で追加されたlist comprehensionsとは異なり、Python 2.7以降で使用できます。 Python 2.2からPython 2.6では、 set()
関数をジェネレータ式で使用して同じ結果を得ることができます。
set(x for x in range(5))
# Out: {0, 1, 2, 3, 4}
条件節を使用して反復的で高価な操作を避ける
以下のリストの理解を考慮してください:
>>> def f(x):
... import time
... time.sleep(.1) # Simulate expensive function
... return x**2
>>> [f(x) for x in range(1000) if f(x) > 10]
[16, 25, 36, ...]
これは、 x
1,000の値に対してf(x)
を2回呼び出すことになります.1つは値を生成するための呼び出しで、もう1つはif
条件を検査することです。 f(x)
が特に高価な演算である場合、これはパフォーマンスに大きな影響を与える可能性があります。悪いことに、 f()
を呼び出すと副作用があると、驚くべき結果が得られます。
代わりに、次のように、中間の反復可能( ジェネレータ式 )を生成することによって、 x
各値に対して高価な演算を1回だけ評価する必要があります。
>>> [v for v in (f(x) for x in range(1000)) if v > 10]
[16, 25, 36, ...]
>>> [v for v in map(f, range(1000)) if v > 10]
[16, 25, 36, ...]
より読みやすいコードをもたらす別の方法は、部分的な結果(前の例ではv
)を繰り返し可能なもの(リストやタプルなど)に入れて、それを反復することです。 v
がiterable内の唯一の要素になるので、結果は1回だけ計算されたslow関数の出力への参照を持つことになります。
>>> [v for x in range(1000) for v in [f(x)] if v > 10]
[16, 25, 36, ...]
しかし、実際には、コードのロジックは複雑になる可能性があり、読みやすいようにすることが重要です。一般に、複雑な1ライナーに対しては、別々のジェネレータ関数が推奨されます。
>>> def process_prime_numbers(iterable):
... for x in iterable:
... if is_prime(x):
... yield f(x)
...
>>> [x for x in process_prime_numbers(range(1000)) if x > 10]
[11, 13, 17, 19, ...]
コンピューティング防止する別の方法f(x)
複数回使用することである@functools.lru_cache()
(Pythonの3.2以上) デコレータにf(x)
。このようにして、入力x
に対するf
の出力は既に1回計算されているので、元のリスト理解の2回目の呼び出しは辞書検索と同じくらい速くなります。この手法では効率を向上させるためにメモ生成を使用しています。これはジェネレータ式を使用する場合と似ています。
あなたがリストを平坦化しなければならないと言う
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
メソッドのいくつかは次のようになります。
reduce(lambda x, y: x+y, l)
sum(l, [])
list(itertools.chain(*l))
しかし、リストの理解は、最も時間の複雑さをもたらすでしょう。
[item for sublist in l for item in sublist]
L個のサブリストがある場合には、+(黙示的に使用される総和を含む)に基づくショートカットは、必要な場合にはO(L ^ 2)です。中間結果リストが長くなるにつれて、各ステップで新しい中間結果リストオブジェクトが取得されます前の中間結果のすべての項目をコピーする必要があります(最後にいくつかの新しい項目が追加されます)。 (簡単にするために、実際の一般性を失わずに)、それぞれI項目のLサブリストがあるとします。最初のI項目は前後にL-1回、2番目のI項目はL-2回などにコピーされます。総コピー数は、1からLまでのxのxの合計、すなわちI *(L ** 2)/ 2の1倍です。
リストの理解はちょうど1つのリストを生成し、各アイテムを(元の居住地から結果リストに)1回もコピーします。
タプルを含む解説
リスト内包のfor
節は、複数の変数を指定できます。
[x + y for x, y in [(1, 2), (3, 4), (5, 6)]]
# Out: [3, 7, 11]
[x + y for x, y in zip([1, 3, 5], [2, 4, 6])]
# Out: [3, 7, 11]
これは通常のfor
ループと似for
ます:
for x, y in [(1,2), (3,4), (5,6)]:
print(x+y)
# 3
# 7
# 11
ただし、理解を開始する式がタプルの場合は、括弧で囲む必要があります。
[x, y for x, y in [(1, 2), (3, 4), (5, 6)]]
# SyntaxError: invalid syntax
[(x, y) for x, y in [(1, 2), (3, 4), (5, 6)]]
# Out: [(1, 2), (3, 4), (5, 6)]
理解を使って出現回数を数える
ある条件を満たす反復可能なアイテムの数を数えたいときは、理解を使って慣用句構文を生成することができます:
# Count the numbers in `range(1000)` that are even and contain the digit `9`:
print (sum(
1 for x in range(1000)
if x % 2 == 0 and
'9' in str(x)
))
# Out: 95
基本的な概念は次のように要約できます。
-
range(1000)
要素を反復処理します。 - すべての必要な連結し
if
の条件を。 - 式に1を使用して、条件を満たす各アイテムに対して1を返します。
- 条件を満たすアイテムの数を決定するために、
1
秒を合計します。
注 :ここでは、リスト内の1
集めていません(角括弧がないことに注意してください)。しかし、それらを合計しているsum
関数に直接渡しています。これはジェネレーター式と呼ばれ、理解度に似ています。
リスト内の型の変更
数量データは、処理する前に数値型に変換する必要がある文字列として読み込まれることがよくあります。すべてのリストアイテムのタイプは、 リスト内包表記またはmap()
関数のいずれかで変換できます。
# Convert a list of strings to integers.
items = ["1","2","3","4"]
[int(item) for item in items]
# Out: [1, 2, 3, 4]
# Convert a list of strings to float.
items = ["1","2","3","4"]
map(float, items)
# Out:[1.0, 2.0, 3.0, 4.0]