web-dev-qa-db-ja.com

このコードのlist [:]の意味は何ですか?

このコードはPythonのドキュメントからのものです。私は少し混乱しています。

words = ['cat', 'window', 'defenestrate']
for w in words[:]:
    if len(w) > 6:
        words.insert(0, w)
print(words)

そして、私が最初に考えたことは次のとおりです。

words = ['cat', 'window', 'defenestrate']
for w in words:
    if len(w) > 6:
        words.insert(0, w)
print(words)

このコードはなぜ無限ループを作成し、最初のループは作成しないのですか?

58
S. Li

これは、落とし穴の1つです!初心者から逃れることができるpythonの。

words[:]はここの魔法のソースです。

観察する:

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words[:]
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['cat', 'window', 'defenestrate']

そして今、[:]なしで:

>>> words =  ['cat', 'window', 'defenestrate']
>>> words2 = words
>>> words2.insert(0, 'hello')
>>> words2
['hello', 'cat', 'window', 'defenestrate']
>>> words
['hello', 'cat', 'window', 'defenestrate']

ここで注意すべき主なことは、words[:]が既存のリストのcopyを返すため、変更されていないコピーを反復処理していることです。

id()を使用して、同じリストを参照しているかどうかを確認できます。

最初の場合:

>>> words2 = words[:]
>>> id(words2)
4360026736
>>> id(words)
4360188992
>>> words2 is words
False

2番目の場合:

>>> id(words2)
4360188992
>>> id(words)
4360188992
>>> words2 is words
True

[i:j]スライス演算子と呼ばれ、インデックスiから始まる(ただし含まない)リストの新しいコピーを返すことに注意してください)インデックスj

したがって、words[0:2]は以下を提供します

>>> words[0:2]
['hello', 'cat']

開始インデックスを省略すると、デフォルトは0になり、最後のインデックスを省略すると、デフォルトはlen(words)になり、最終結果は全体リストのコピーを受け取ることになります。


コードをもう少し読みやすくしたい場合は、copyモジュールをお勧めします。

from copy import copy 

words = ['cat', 'window', 'defenestrate']
for w in copy(words):
    if len(w) > 6:
        words.insert(0, w)
print(words)

これは基本的に最初のコードスニペットと同じことを行い、はるかに読みやすくなります。

別の方法として(DSMのコメントで言及されているように)およびpython> = 3で、同じことを行うwords.copy()を使用することもできます。

83
cs95

words[:]は、wordsのすべての要素を新しいリストにコピーします。したがって、words[:]を反復処理すると、実際にwordsが現在持っているすべての要素を反復処理していることになります。したがって、wordsを変更すると、それらの変更の効果はwords[:]に表示されません(wordsを変更する前にwords[:]を呼び出したため)

後者の例では、wordsを繰り返し処理しています。つまり、wordsに加えた変更は、実際にイテレーターに表示されます。その結果、wordsのインデックス0に挿入すると、wordsの他のすべての要素が1つのインデックスで「バンプ」されます。したがって、forループの次の反復に移動すると、wordsの次のインデックスにある要素を取得しますが、それは今見たばかりの要素です(最初に要素を挿入したため)リストの他のすべての要素をインデックスだけ上に移動します)。

これを実際に見るには、次のコードを試してください。

words = ['cat', 'window', 'defenestrate']
for w in words:
    print("The list is:", words)
    print("I am looking at this Word:", w)
    if len(w) > 6:
        print("inserting", w)
        words.insert(0, w)
        print("the list now looks like this:", words)
print(words)
11
inspectorG4dget

(@Coldspeedの答えに加えて)

以下の例を見てください:

words = ['cat', 'window', 'defenestrate']
words2 = words
words2 is words

結果:True

名前Wordwords2は同じオブジェクトを参照することを意味します。

words = ['cat', 'window', 'defenestrate']
words2 = words[:]
words2 is words

結果:False

この場合、新しいオブジェクトを作成しました。

5
Denis Kuzin

イテレータとイテラブルを見てみましょう。

反復可能オブジェクトは、イテレータを返す__iter__メソッドを持つオブジェクト、またはゼロから始まる連続インデックスを取得できる__getitem__メソッドを定義するオブジェクトです(インデックスが無効になったときにIndexErrorを発生させます) 。そのため、反復可能オブジェクトは、反復子を取得できるオブジェクトです。

イテレータは、next(Python 2)または__next__(Python 3)メソッドを持つオブジェクトです。

iter(iterable)はイテレータオブジェクトを返し、list_obj[:]は新しいリストオブジェクト、list_objectの正確なコピーを返します。

最初の場合:

for w in words[:]

forループは、元の単語ではなく、リストの新しいコピーを反復処理します。単語の変更はループの繰り返しには影響せず、ループは正常に終了します。

これが、ループの動作方法です。

  1. ループはiterableでiterメソッドを呼び出し、イテレーターを反復処理します

  2. ループは、イテレータオブジェクトでnextメソッドを呼び出して、イテレータから次のアイテムを取得します。このステップは、要素がなくなるまで繰り返されます

  3. StopIteration例外が発生すると、ループは終了します。

2番目の場合:

words = ['cat', 'window', 'defenestrate']
for w in words:
    if len(w) > 6:
        words.insert(0, w)
print(words)

元のリストの単語を繰り返し処理し、単語に要素を追加すると、反復子オブジェクトに直接影響します。したがって、単語が更新されるたびに、対応する反復子オブジェクトも更新されるため、無限ループが作成されます。

これを見てください:

>>> l = [2, 4, 6, 8]
>>> i = iter(l) # returns list_iterator object which has next method
>>> next(i)
2
>>> next(i)
4
>>> l.insert(2, 'A')
>>> next(i)
'A'

StopIterationの前に元のリストを更新するたびに、更新された反復子を取得し、それに応じてnextが返されます。それがループが無限に実行される理由です。

反復と反復プロトコルの詳細については、 here を参照してください。

1
Bibek Ghimire