私はPythonのタプルのリストを繰り返し処理しており、特定の基準を満たしている場合は削除しようとしています。
for tup in somelist:
if determine(tup):
code_to_remove_tup
代わりに何を使うべきcode_to_remove_tup
ですか?この方法でアイテムを削除する方法がわかりません。
私はPythonのタプルのリストを繰り返し処理しており、特定の基準を満たしている場合は削除しようとしています。
for tup in somelist:
if determine(tup):
code_to_remove_tup
代わりに何を使うべきcode_to_remove_tup
ですか?この方法でアイテムを削除する方法がわかりません。
回答:
リスト内包表記を使用して、削除したくない要素のみを含む新しいリストを作成できます。
somelist = [x for x in somelist if not determine(x)]
または、スライスsomelist[:]
に割り当てることにより、既存のリストを変更して、必要なアイテムのみを含めることができます。
somelist[:] = [x for x in somelist if not determine(x)]
このアプローチはsomelist
、変更を反映する必要がある他の参照がある場合に役立ちます。
理解の代わりに、を使用することもできますitertools
。Python 2の場合:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
またはPython 3の場合:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
わかりやすくするため、そして[:]
ハックやファジーという表記の使用を見つけた人のために、より明示的な代替案を示します。理論的には、空間と時間に関しては、上記のワンライナーと同じです。
temp = []
while somelist:
x = somelist.pop()
if not determine(x):
temp.append(x)
while temp:
somelist.append(templist.pop())
また、Pythonリストのアイテムの置換機能を持たない他の言語でも、最小限の変更で機能します。たとえば、すべての言語がFalse
Pythonのように空のリストをにキャストするわけではありません。のwhile somelist:
ようなより明示的なものの代わりに使用できwhile len(somelist) > 0:
ます。
somelist[:] = (x for x in somelist if determine(x))
これを使用すると、不要なコピーを作成しない可能性があるジェネレータが作成されます。
list_ass_slice()
を実装する関数。この関数は常にリストを返します。つまり、ジェネレーターの代わりにリストをすでに使用している@Alex Martelliのソリューションは、おそらくより効率的ですsomelist[:]=
PySequence_Fast()
somelist
は両方の方法で変更されませんか?
リストの内包を示唆する回答はほぼ正しいです。ただし、完全に新しいリストを作成し、古いリストと同じ名前を付けた場合を除き、古いリストは変更されません。@Lennartの提案のように、選択的削除によって行うこととは異なります-高速ですが、複数の参照を介してリストにアクセスする場合、参照の1つを再配置し、リストオブジェクトを変更しないという事実です。それ自体が、微妙で悲惨なバグにつながる可能性があります。
幸いにも、リスト内包の速度と必要なインプレース変更のセマンティクスの両方を取得するのは非常に簡単です。コードを書くだけです。
somelist[:] = [tup for tup in somelist if determine(tup)]
他の答えとの微妙な違いに注意してください:これはベアネームに割り当てられていません-たまたまリスト全体であるリストスライスに割り当てられているため、1つの参照を再配置するのではなく、同じPythonリストオブジェクト内のリストコンテンツ を置き換えます(前のリストオブジェクトから新しいリストオブジェクトへ)他の回答と同様です。
a
はb
、を使用してくださいa.clear(); a.update(b)
。
x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
これx
はリスト内包の結果に再割り当てしy
ますが、元のリストを引き続き参照します['foo','bar','baz']
。同じリストを期待x
しy
て参照している場合は、バグが発生している可能性があります。これを防ぐには、Alexが示すように、リスト全体のスライスに割り当てますx = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
。リストが適切に変更されます。リストへのすべての参照(ここx
と両方y
)が新しいリストを参照していることを確認します。
filter
関数を使用しても新しいリストが作成され、要素は変更されません...ただolist[:] = [i for i in olist if not dislike(i)]
リストのコピーを取り、それを最初に反復する必要があります。そうしないと、反復が失敗し、予期しない結果になる可能性があります。
たとえば(リストの種類によって異なります):
for tup in somelist[:]:
etc....
例:
>>> somelist = range(10)
>>> for x in somelist:
... somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]
>>> somelist = range(10)
>>> for x in somelist[:]:
... somelist.remove(x)
>>> somelist
[]
list(somelist)
イテラブルをリストに変換します。somelist[:]
スライスをサポートするオブジェクトのコピーを作成します。したがって、彼らは必ずしも同じことをするわけではありません。この場合、somelist
オブジェクトのコピーを作成したいので、以下を使用します[:]
remove()
反復ごとにWHOLEリスト全体を調べる必要があるため、時間がかかります。
for i in range(len(somelist) - 1, -1, -1):
if some_condition(somelist, i):
del somelist[i]
後方に移動する必要があります。そうしないと、座っている木の枝を切り取るような感じです:-)
Pythonの2人のユーザー:交換するrange
ことでxrange
、ハードコードのリストを作成しないように
reversed()
組み込み
m
速度が遅くなる場合があります。
公式のPython 2チュートリアル4.2。「ステートメント用」
https://docs.python.org/2/tutorial/controlflow.html#for-statements
ドキュメントのこの部分により、次のことが明確になります。
[:]
ループ内で反復しているシーケンスを変更する必要がある場合(たとえば、選択したアイテムを複製するため)、最初にコピーを作成することをお勧めします。シーケンスを反復しても、暗黙的にコピーは作成されません。スライス表記はこれを特に便利にします:
>>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']
Python 2ドキュメント7.3。「forステートメント」
https://docs.python.org/2/reference/compound_stmts.html#for
ドキュメントのこの部分は、コピーを作成する必要があることをもう一度言い、実際の削除例を示します:
注:シーケンスがループによって変更されているときは微妙です(これは、変更可能なシーケンス、つまりリストでのみ発生します)。次に使用される項目を追跡するために内部カウンターが使用され、これは反復ごとに増分されます。このカウンターがシーケンスの長さに達すると、ループが終了します。つまり、スイートがシーケンスから現在の(または前の)アイテムを削除すると、次のアイテムはスキップされます(既に処理された現在のアイテムのインデックスを取得するため)。同様に、スイートがシーケンス内の現在のアイテムの前にアイテムを挿入した場合、現在のアイテムはループを介して次回処理されます。これは、シーケンス全体のスライスを使用して一時的なコピーを作成することで回避できる厄介なバグにつながる可能性があります。たとえば、
for x in a[:]: if x < 0: a.remove(x)
ただし、値を見つけるためにリスト全体.remove()
を反復処理する必要があるため、この実装には同意しません。
最善の回避策
どちらか:
新しいアレイを最初から開始.append()
し、最後に戻します:https : //stackoverflow.com/a/1207460/895245
この時間は効率的ですが、反復中に配列のコピーを保持するため、スペース効率は低くなります。
del
インデックスで使用:https : //stackoverflow.com/a/1207485/895245
配列のコピーが不要になるため、スペース効率が向上しますが、CPythonリストは動的配列で実装されるため、時間効率が低下します。
つまり、アイテムを削除するには、次のすべてのアイテムを1つ戻す必要があります。つまり、O(N)です。
一般的に.append()
は、メモリが大きな問題でない限り、デフォルトでより高速なオプションを選択するだけです。
Pythonはこれをもっとうまくできますか?
この特定のPython APIは改善できるようです。たとえば、以下と比較してください。
std::vector::erase
削除された要素に有効なインタレータを返すC ++どちらを使用しても、イテレータ自体を除いて、反復されているリストを変更できないことは明らかであり、リストをコピーせずに効率的に変更できます。
おそらく、根本的な根拠は、Pythonリストは動的配列に基づいていると想定されているため、Javaはとの両方の実装ArrayList
とのインターフェース階層が優れている一方で、いずれのタイプの削除も時間効率が悪いというLinkedList
ことですListIterator
。
Python stdlibにも明示的なリンクリストタイプがないようです:Python Linked List
そのような例に対するあなたの最善のアプローチは、リストの理解です
somelist = [tup for tup in somelist if determine(tup)]
determine
関数を呼び出すよりも複雑なことをしている場合は、新しいリストを作成し、それに合わせて追加するだけです。例えば
newlist = []
for tup in somelist:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
somelist = newlist
を使用してリストをコピーするremove
と、以下の回答の1つで説明されているように、コードが少しきれいに見える場合があります。これは、最初にリスト全体をコピーし、O(n)
remove
削除される各要素に対して操作を実行してこれをO(n^2)
アルゴリズムにするため、極端に大きなリストでは絶対に行わないでください。
for tup in somelist[:]:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
関数型プログラミングが好きな人のために:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
または
from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
filter
、よりPythonicです。2. またはlambda
を使用する必要がある場合は、リストcompまたはgenexprが常に優れたオプションです。また、transform / predicate関数がCで実装されたPython組み込みであり、反復可能オブジェクトがそれほど小さくない場合は少し速くなりますが、listcomp / genexprで回避できる必要がある場合は常に遅くなります。map
filter
map
filter
lambda
膨大なリストを使用してこれを行う必要があり、特に私の場合、残っているアイテムと比較して削除の数が少ないため、リストを複製することはコストがかかるように思われました。私はこの低レベルのアプローチを採用しました。
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
私が知らないのは、いくつかの削除を大きなリストをコピーするのに比べてどれほど効率的かということです。何か洞察があればコメントしてください。
list
も、リストの中央からの削除にはリストの長さの直線的な時間がかかるので、そもそもデータ構造としての選択は慎重に検討する必要があります。k番目のシーケンシャルアイテムへのランダムアクセスが本当に必要ない場合は、おそらく検討してOrderedDict
ください。
newlist = []
、その後、およびnewlist.append(array[i])
直前del array[i]
?
list()
リンクリストの場合、ランダムアクセスはコストがかかります。list()
配列の場合、後続のすべての要素を前に移動する必要があるため、削除はコストがかかります。まともなイテレータは、リンクリストの実装に適しています。ただし、これはスペース効率に優れています。
現在のリストアイテムが目的の基準を満たしている場合は、新しいリストを作成することもできます。
そう:
for item in originalList:
if (item != badValue):
newList.append(item)
また、プロジェクト全体を新しいリスト名で再コーディングする必要がないようにします。
originalList[:] = newList
注意、Pythonのドキュメントから:
copy.copy(x)xの浅いコピーを返します。
copy.deepcopy(x)xのディープコピーを返します。
この回答はもともと、重複としてマークされている質問への応答として書かれました: pythonのリストから座標を削除しています
コードには2つの問題があります。
1)remove()を使用すると、整数を削除しようとしますが、タプルを削除する必要があります。
2)forループはリストの項目をスキップします。
コードを実行するとどうなるか見てみましょう。
>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
... if a < 0 or b < 0:
... L1.remove(a,b)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)
最初の問題は、 'a'と 'b'の両方をremove()に渡していますが、remove()は単一の引数しか受け入れないことです。それでは、remove()をリストで適切に機能させるにはどうすればよいですか?リストの各要素を理解する必要があります。この場合、それぞれがタプルです。これを確認するには、リストの1つの要素にアクセスしてみましょう(インデックス付けは0から始まります)。
>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>
ああ!L1の各要素は実際にはタプルです。これが、remove()に渡す必要があるものです。Pythonのタプルは非常に簡単で、括弧で値を囲むことで簡単に作成できます。「a、b」はタプルではありませんが、「(a、b)」はタプルです。したがって、コードを変更して再度実行します。
# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))
このコードはエラーなしで実行されますが、出力されるリストを見てみましょう。
L1 is now: [(1, 2), (5, 6), (1, -2)]
(1、-2)がまだリストにあるのはなぜですか?ループを使用してリストを反復しながらリストを変更することは、特別な注意なしに非常に悪い考えです。(1、-2)がリストに残る理由は、リスト内の各項目の位置がforループの反復間で変更されたためです。上記のコードに長いリストをフィードするとどうなるか見てみましょう。
L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
その結果から推測できるように、条件ステートメントがtrueと評価されてリストアイテムが削除されるたびに、ループの次の反復では、リスト内の次のアイテムの評価がスキップされます。これは、その値が異なるインデックスに配置されているためです。
最も直感的なソリューションは、リストをコピーしてから、元のリストを反復処理し、コピーのみを変更することです。次のようにしてみてください:
L2 = L1
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)
ただし、出力は以前と同じになります。
'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
これは、L2を作成したときに、Pythonが実際に新しいオブジェクトを作成しなかったためです。代わりに、L2をL1と同じオブジェクトを参照するだけでした。これは、単に「等しい」(==)とは異なる「is」で確認できます。
>>> L2=L1
>>> L1 is L2
True
copy.copy()を使用して真のコピーを作成できます。その後、すべてが期待どおりに動作します。
import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
最後に、L1の完全に新しいコピーを作成する必要がある場合よりも明確な解決策が1つあります。reverse()関数:
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
if a < 0 or b < 0 :
L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
残念ながら、reversed()がどのように機能するかを十分に説明することはできません。リストが渡されると、「listreverseiterator」オブジェクトを返します。実用上は、これを引数の逆のコピーを作成すると考えることができます。これが私がお勧めするソリューションです。
イテレーション中に他に何かしたい場合は、インデックス(たとえば、dictsのリストがある場合にそれを参照できることを保証します)と実際のリストアイテムの内容の両方を取得するとよいでしょう。
inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]
for idx, i in enumerate(inlist):
do some stuff with i['field1']
if somecondition:
xlist.append(idx)
for i in reversed(xlist): del inlist[i]
enumerate
アイテムとインデックスに一度にアクセスできます。reversed
後で削除するインデックスが変更されないようにするためです。
forループを逆に試すことができるので、some_listの場合は次のようにします。
list_len = len(some_list)
for i in range(list_len):
reverse_i = list_len - 1 - i
cur = some_list[reverse_i]
# some logic with cur element
if some_condition:
some_list.pop(reverse_i)
この方法では、インデックスが整列され、リストの更新の影響を受けません(cur要素をポップするかどうかに関係なく)。
reversed(list(enumerate(some_list)))
は、自分でインデックスを計算するよりも簡単です。
考えられる解決策の1つは、いくつかのことを削除するだけでなく、すべての要素を1つのループで処理したい場合に役立ちます。
alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
if x == 'bad':
alist.pop(i)
i -= 1
# do something cool with x or just print x
print(x)
i += 1
bad
物事を削除し、それを使って何かを行いgood
、1つのループで何かをしたい場合はどうすればよいですか?
alist[:]
)を使ってリストのコピーを作成するという点で、多少の賢さがあることに気付きました。良いリビジョンは良いです。私の賛成票を取ってください。
私は同様のことをする必要があり、私の場合、問題はメモリでした-リスト内の複数のデータセットオブジェクトをいくつかのことを行った後、新しいオブジェクトとしてマージする必要があり、マージする各エントリを取り除く必要がありましたそれらのすべてを複製してメモリを大量に消費することは避けてください。私の場合、リストの代わりに辞書にオブジェクトを置くとうまくいきました:
「」
k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}
print d
for i in range(5):
print d[i]
d.pop(i)
print d
「」
TLDR:
私はこれを可能にするライブラリを書きました:
from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)
for tup in fSomeList:
if determine(tup):
# remove 'tup' without "breaking" the iteration
fSomeList.remove(tup)
# tup has also been removed from 'someList'
# as well as 'fSomeList'
可能であれば、反復処理中に反復可能オブジェクトを変更する必要のない別の方法を使用するのが最善ですが、アルゴリズムによっては、それほど単純ではない場合があります。したがって、元の質問で説明されているコードパターンが本当に必要な場合は、それが可能です。
リストだけでなく、すべての変更可能なシーケンスで機能するはずです。
完全な答え:
編集:この回答の最後のコード例は、リスト内包表記を使用するのではなく、リストを所定の位置に変更したい場合があるユースケースを示しています。回答の最初の部分は、配列を適切に変更する方法のチュートリアルとして機能します。
解決策は、senderleからの(関連する質問に対する)この回答から続きます。これは、変更されたリストを反復処理しているときに配列インデックスがどのように更新されるかを説明しています。以下のソリューションは、リストが変更された場合でも配列のインデックスを正しく追跡するように設計されています。
ここfluidIter.py
からダウンロードしてください https://github.com/alanbacon/FluidIterator
。これは単一のファイルなので、gitをインストールする必要はありません。インストーラーはないので、ファイルが自分のpythonパスにあることを確認する必要があります。コードはpython 3用に作成されており、python 2ではテストされていません。
from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]
fluidL = FluidIterable(l)
for i in fluidL:
print('initial state of list on this iteration: ' + str(fluidL))
print('current iteration value: ' + str(i))
print('popped value: ' + str(fluidL.pop(2)))
print(' ')
print('Final List Value: ' + str(l))
これにより、次の出力が生成されます。
initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2
initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3
initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4
initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5
initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6
initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7
initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8
Final List Value: [0, 1]
上記ではpop
、流体リストオブジェクトに対してメソッドを使用しました。他の一般的な反復可能な方法はまた、として実装されていますdel fluidL[i]
、.remove
、.insert
、.append
、.extend
。リストはまた、スライスを使用して変更することができる(sort
及びreverse
方法が実装されていません)。
唯一の条件は、任意の時点で、fluidL
またはl
別のリストオブジェクトに再度割り当てられた場合にコードが機能しなかった場合に、リストを変更する必要があることだけです。元のfluidL
オブジェクトは引き続きforループで使用されますが、変更する範囲外になります。
すなわち
fluidL[2] = 'a' # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8] # is not OK
リストの現在のインデックス値にアクセスする場合は、列挙を使用できません。これは、forループが実行された回数を数えるだけだからです。代わりに、イテレータオブジェクトを直接使用します。
fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
print('enum: ', i)
print('current val: ', v)
print('current ind: ', fluidArrIter.currentIndex)
print(fluidArr)
fluidArr.insert(0,'a')
print(' ')
print('Final List Value: ' + str(fluidArr))
これは以下を出力します:
enum: 0
current val: 0
current ind: 0
[0, 1, 2, 3]
enum: 1
current val: 1
current ind: 2
['a', 0, 1, 2, 3]
enum: 2
current val: 2
current ind: 4
['a', 'a', 0, 1, 2, 3]
enum: 3
current val: 3
current ind: 6
['a', 'a', 'a', 0, 1, 2, 3]
Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]
このFluidIterable
クラスは、元のリストオブジェクトのラッパーを提供するだけです。元のオブジェクトには、次のように流動オブジェクトのプロパティとしてアクセスできます。
originalList = fluidArr.fixedIterable
その他の例/テストはif __name__ is "__main__":
、の下部のセクションにありfluidIter.py
ます。これらは、さまざまな状況で何が起こるかを説明しているため、一見の価値があります。例:スライスを使用してリストの大きなセクションを置き換える。または、ネストされたforループで同じイテラブルを使用(および変更)します。
最初に述べたように、これはコードの可読性を損ない、デバッグをより困難にする複雑なソリューションです。したがって、David Raznickの回答で言及されているリスト内包表記など、他のソリューションを最初に検討する必要があります。そうは言っても、このクラスが私にとって便利で、削除が必要な要素のインデックスを追跡するよりも使いやすいクラスを見つけました。
編集:コメントで述べたように、この回答はこのアプローチが解決策を提供する問題を実際に提示するものではありません。私はここでそれに対処しようとします:
リスト内包表記は新しいリストを生成する方法を提供しますが、これらのアプローチは、リスト全体の現在の状態ではなく、各要素を個別に調べる傾向があります。
すなわち
newList = [i for i in oldList if testFunc(i)]
しかし、結果がすでにtestFunc
追加されている要素に依存している場合はどうnewList
でしょうか?または、まだその中にある要素がoldList
次に追加される可能性がありますか?リスト内包表記を使用する方法はまだあるかもしれませんが、それは優雅さを失い始めるでしょう、そして私にとっては適切なリストを修正する方が簡単だと感じます。
以下のコードは、上記の問題に悩まされているアルゴリズムの一例です。アルゴリズムはリストを減らし、要素が他の要素の倍数にならないようにします。
randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
print(' ')
print('outer val: ', i)
innerIntsIter = fRandInts.__iter__()
for j in innerIntsIter:
innerIndex = innerIntsIter.currentIndex
# skip the element that the outloop is currently on
# because we don't want to test a value against itself
if not innerIndex == fRandIntsIter.currentIndex:
# if the test element, j, is a multiple
# of the reference element, i, then remove 'j'
if j%i == 0:
print('remove val: ', j)
# remove element in place, without breaking the
# iteration of either loop
del fRandInts[innerIndex]
# end if multiple, then remove
# end if not the same value as outer loop
# end inner loop
# end outerloop
print('')
print('final list: ', randInts)
出力と最終的な縮小リストを以下に示します
outer val: 70
outer val: 20
remove val: 80
outer val: 61
outer val: 54
outer val: 18
remove val: 54
remove val: 18
outer val: 7
remove val: 70
outer val: 55
outer val: 9
remove val: 18
final list: [20, 61, 7, 55, 9]
some_list[:] = [x for x in some_list if not some_condition(x)]
できないことは何ですか?それに対する答えがなければ、タイプミスとコメントアウトされたコードを備えた600行のライブラリをダウンロードして使用する方が、ワンライナーよりも問題の解決策として優れていると誰もが信じるべきでしょうか?-1。
some_list[:] = [x for x in some_list if not some_condition(y)]
でy
は、where がとは異なるリスト要素のようなものを書くことはできませんx
。書くこともできませんsome_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
。
最も効果的な方法はリストを理解することであり、多くの人が自分のケースを示します。もちろん、それiterator
を通過するための良い方法でもありfilter
ます。
Filter
関数とシーケンスを受け取ります。Filter
順番に各要素に渡された関数を適用し、その後、関数の戻り値であるか否かに応じて、要素を保持するか破棄するかどうかを決定しTrue
、またはFalse
。
例があります(タプルのオッズを取得):
list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))
# result: [1, 5, 9, 15]
注意:イテレータを処理することもできません。イテレータはシーケンスよりも優れている場合があります。
forループはインデックスを反復します。
あなたがリストを持っていると考えて、
[5, 7, 13, 29, 65, 91]
と呼ばれるリスト変数を使用していますlis
。同じものを使用して削除します。
あなたの変数
lis = [5, 7, 13, 29, 35, 65, 91]
0 1 2 3 4 5 6
5回目の反復の間、
あなたの35番は、あなたがそれをリストから削除プライムそうではなかったです。
lis.remove(y)
次に、次の値(65)が前のインデックスに移動します。
lis = [5, 7, 13, 29, 65, 91]
0 1 2 3 4 5
そのため、4番目の反復の完了ポインタは5番目に移動しました。
これが、前のインデックスに移動してからループが65をカバーしない理由です。
したがって、コピーではなくオリジナルを参照する別の変数にリストを参照しないでください。
ite = lis #dont do it will reference instead copy
ので、リストのコピーを行います list[::]
今あなたはそれを与えるでしょう、
[5, 7, 13, 29]
問題は、反復中にリストから値を削除すると、リストのインデックスが崩れることです。
代わりに理解力を試すことができます。
リスト、タプル、辞書、文字列など、すべての反復可能をサポートしています
他の回答は正解です。通常、反復しているリストから削除することは悪い考えです。逆反復は落とし穴を回避しますが、それを行うコードに従うのははるかに難しいため、通常はリスト内包表記またはを使用する方がよいでしょうfilter
。
ただし、反復しているシーケンスから要素を削除しても安全なケースが1つあります。それは、反復中に1つのアイテムのみを削除する場合です。これは、return
またはを使用して確認できますbreak
。例えば:
for i, item in enumerate(lst):
if item % 4 == 0:
foo(item)
del lst[i]
break
これは、リストの最初のアイテムに何らかの条件を満たす副作用を伴う操作を実行し、その直後にそのアイテムをリストから削除する場合に、リスト内包よりも理解しやすいことがよくあります。
あなたの問題を解決するための3つのアプローチを考えることができます。例として、タプルのランダムなリストを作成しますsomelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
。私が選ぶ条件はsum of elements of a tuple = 15
です。最後のリストには、合計が15に等しくないタプルのみが含まれます。
私が選んだのは、ランダムに選ばれた例です。タプルのリストと選択した条件を自由に変更してください。
方法1.>提案したフレームワークを使用します(forループ内のコードを埋めます)。del
上記の条件を満たすタプルを削除するのに小さなコードを使用します。ただし、このメソッドは、2つの連続して配置されたタプルが指定された条件を満たす場合、(上記の条件を満たす)タプルを見落とします。
for tup in somelist:
if ( sum(tup)==15 ):
del somelist[somelist.index(tup)]
print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]
方法2.>指定された条件が満たされない要素(タプル)を含む新しいリストを作成します(これは、指定された条件が満たされたリストの要素を削除するのと同じことです)。以下はそのためのコードです:
newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]
print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
方法3.>与えられた条件が満たされているインデックスを見つけ、それらのインデックスに対応する要素の削除(タプル)を使用します。以下はそのためのコードです。
indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]
print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
方法1と方法2は方法3よりも高速です。Method2とmethod3はmethod1よりも効率的です。私はmethod2を好みます。前述の例では、time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
リストを一度に1項目ずつフィルタリングするだけではない状況では、反復中に反復を変更する必要があります。
これは、事前にリストをコピーすることが正しくなく、逆反復が不可能で、リストの理解もオプションではない例です。
""" Sieve of Eratosthenes """
def generate_primes(n):
""" Generates all primes less than n. """
primes = list(range(2,n))
idx = 0
while idx < len(primes):
p = primes[idx]
for multiple in range(p+p, n, p):
try:
primes.remove(multiple)
except ValueError:
pass #EAFP
idx += 1
yield p
数のリストを立ち上げ、3で割り切れるすべてのnoを削除したい
list_number =[i for i in range(100)]
を使用してlist comprehension
、これは新しいリストを処理し、新しいメモリ空間を作成します
new_list =[i for i in list_number if i%3!=0]
lambda filter
関数を使用して、結果の新しいリストを作成し、メモリ空間を消費します
new_list = list(filter(lambda x:x%3!=0, list_number))
新しいリストのメモリスペースを消費することなく、既存のリストを変更
for index, value in enumerate(list_number):
if list_number[index]%3==0:
list_number.remove(value)