319

Python では、次のように、リスト内包表記に複数の反復子を含めることができます。

[(x,y) for x in a for y in b]

いくつかの適切なシーケンス a および b。私は、Python のリスト内包表記の入れ子になったループのセマンティクスを認識しています。

私の質問は次のとおりです: 内包内の 1 つの反復子が他の反復子を参照できますか? 言い換えれば、次のようなものがありますか:

[x for x in a for a in b]

外側のループの現在の値は、内側の反復子ですか?

例として、ネストされたリストがある場合:

a=[[1,2],[3,4]]

この結果を達成するためのリスト内包表記は次のようになります。

[1,2,3,4]

?? (これは私が知りたいことなので、理解できる回答のみをリストしてください)。

4

11 に答える 11

192

あなた自身の提案であなたの質問に答えるには:

>>> [x for b in a for x in b] # Works fine

リスト内包表記の回答を求められましたが、優れた itertools.chain() についても指摘させてください。

>>> from itertools import chain
>>> list(chain.from_iterable(a))
>>> list(chain(*a)) # If you're using python < 2.6
于 2009-07-29T08:36:21.497 に答える
148

ええ、私はアンサーを見つけたと思います。どのループが内側で、どのループが外側であるかについて十分に注意していませんでした。リスト内包表記は次のようになります。

[x for b in a for x in b]

はい、1 つの現在の値を次のループの反復子にすることができます。

于 2009-07-29T08:34:28.707 に答える
63

イテレータの順序は直感に反するように見えるかもしれません。

たとえば、次のとおりです。[str(x) for i in range(3) for x in foo(i)]

それを分解しましょう:

def foo(i):
    return i, i + 0.5

[str(x)
    for i in range(3)
        for x in foo(i)
]

# is same as
for i in range(3):
    for x in foo(i):
        yield str(x)
于 2016-10-05T09:39:42.327 に答える
25

ThomasH はすでに良い回答を追加していますが、何が起こるかを示したいと思います:

>>> a = [[1, 2], [3, 4]]
>>> [x for x in b for b in a]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined

>>> [x for b in a for x in b]
[1, 2, 3, 4]
>>> [x for x in b for b in a]
[3, 3, 4, 4]

Python はリスト内包表記を左から右に解析すると思います。つまり、最初forに発生したループが最初に実行されます。

これの2番目の「問題」はb、リスト内包表記から「漏れる」ことです。最初の成功したリストの理解の後b == [3, 4]

于 2014-10-21T06:47:04.147 に答える
23

この記憶術は私を大いに助けてくれます:

[ <RETURNED_VALUE> <OUTER_LOOP1> <INNER_LOOP2> <INNER_LOOP3> ... <OPTIONAL_IF> ]

これで、 Return + Outer -loop が唯一の正しい順序あると考えることができます。

上記のことを知っていると、3 つのループの場合でもリスト全体の順序は簡単に見えます。


c=[111, 222, 333]
b=[11, 22, 33]
a=[1, 2, 3]

print(
  [
    (i, j, k)                            # <RETURNED_VALUE> 
    for i in a for j in b for k in c     # in order: loop1, loop2, loop3
    if i < 2 and j < 20 and k < 200      # <OPTIONAL_IF>
  ]
)
[(1, 11, 111)]

上記は単なるものだからです:

for i in a:                         # outer loop1 GOES SECOND
  for j in b:                       # inner loop2 GOES THIRD
    for k in c:                     # inner loop3 GOES FOURTH
      if i < 2 and j < 20 and k < 200:
        print((i, j, k))            # returned value GOES FIRST

ネストされたリスト/構造を1つ反復する場合、テクニックは同じです:a質問から:

a = [[1,2],[3,4]]
[i2    for i1 in a      for i2 in i1]
which return [1, 2, 3, 4]

互いに入れ子になったレベル

a = [[[1, 2], [3, 4]], [[5, 6], [7, 8, 9]], [[10]]]
[i3    for i1 in a      for i2 in i1     for i3 in i2]
which return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

等々

于 2020-01-01T11:47:42.547 に答える
13

最初の試みでは、二重リスト内包表記を書くことができませんでした。PEP202を読むと、英語で読むのとは逆の方法で実装されていることが理由であることがわかりました。幸いなことに、これは論理的に適切な実装であるため、構造を理解すれば、非常に簡単に正しく理解できます。

a、b、c、d を連続してネストされたオブジェクトとします。私にとって、リストの理解を拡張する直感的な方法は、英語を模倣することです。

# works
[f(b) for b in a]
# does not work
[f(c) for c in b for b in a]
[f(c) for c in g(b) for b in a]
[f(d) for d in c for c in b for b in a]

言い換えれば、下から上に読むことになります。

# wrong logic
(((d for d in c) for c in b) for b in a)

ただし、これはPython がネストされたリストを実装する方法ではありません。代わりに、実装は最初のチャンクを完全に分離したものとして扱い、次にfors とins を (ボトムアップではなく) トップダウンで 1 つのブロックにチェーンします。つまり、

# right logic
d: (for b in a, for c in b, for d in c)

for d in c最も深いネスト レベル ( ) は、リスト内の最終オブジェクトから最も遠い( )ことに注意してくださいd。この理由はGuido 自身によるものです。

[... for x... for y...]ネストされた for ループのように、フォームはネストされ、最後のインデックスが最も速く変化します。

Skam のテキストの例を使用すると、これはさらに明確になります。

# word: for sentence in text, for word in sentence
[word for sentence in text for word in sentence]

# letter: for sentence in text, for word in sentence, for letter in word
[letter for sentence in text for word in sentence for letter in word]

# letter:
#     for sentence in text if len(sentence) > 2, 
#     for word in sentence[0], 
#     for letter in word if letter.isvowel()
[letter for sentence in text if len(sentence) > 2 for word in sentence[0] for letter in word if letter.isvowel()]
于 2021-04-19T15:35:58.207 に答える
6

こちらの方が分かりやすいと思います

[row[i] for row in a for i in range(len(a))]

result: [1, 2, 3, 4]
于 2015-03-09T12:27:20.180 に答える