質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1894閲覧

pythonのforの処理中にリストをいじると?

hajifu

総合スコア88

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2019/06/07 18:28

#わからないこと
Python学習中のものです。
Pythonのループ処理について、根本的なことかもしれないのですが教えていただきたいです。
今、ループのinのあとにリストを指定して、ループの処理の最中にそのリストをいじるようなプログラムを作っていました。

具体的には、リストの中身をすべて参照して、条件に合うものはリストから削除するというプログラムです。
しかし実際に作っているものがうまくいってなさそうなので、下記のようなシンプルなコードで試してみました。

python

1lists = [i for i in range(10)] # [0~9までのリスト] 2cnt = 0 3 4for i in lists: 5 # 奇数なら削除 6 if i % 2 == 1: 7 lists.remove(i) 8 cnt += 1

すると、listsの中身はちゃんと[0, 2, 4, 6, 8]になるのですが
cntの値が5になってしまいます。
これが10にならないのはどういうことでしょうか。

これはどういったことが起こっているのでしょうか?
途中でリストのインデックスがずれて、参照していない中身があるということなのでしょうか?

また、もしこのようにリストの中で条件に合うものを削除したい場合はどのようにするのがよいのでしょうか?
削除するモノリストを別に用意して、あとでまとめて削除するとか・・・?
宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

8. 複合文 (compound statement) — Python 3.7.3 ドキュメント

の注釈以降を読んでください。全文引用します。

注釈 ループ中でのシーケンスの変更には微妙な問題があります (これはミュータブルなシーケンスのみ、例えばリストで起こり得ます)。 どの要素が次に使われるかを追跡するために、内部的なカウンタが使われており、このカウンタは反復のたびに加算されます。 このカウンタがシーケンスの長さに達すると、ループは終了します。 このことから、スイートの中でシーケンスから現在の (または以前の) 要素を除去すると、(次の要素の位置が、既に処理済みの現在の要素のインデックスになるために) 次の要素が飛ばされることになります。 同様に、スイートの中でシーケンス中の現在の要素以前に要素を挿入すると、現在の要素がループの次の週で再度扱われることになります。 こうした仕様は、厄介なバグにつながります。 これは、シーケンス全体のスライスを使って一時的なコピーを作ることで避けられます。 例えば次のようにします:

python

1for x in a[:]: 2 if x < 0: a.remove(x)

実際には、ループ対象のlistを直接いじるようなコードは「書かない」という方針を前提とするのがいいでしょう。

内包表記を使って、単に一行で

python

1lst = [i for i in range(10) if i % 2 == 0]

とするか、

python

1lst = [] 2for i in range(10): 3 if i % 2 == 0: 4 lst.append(i)

として新しく作るのがいいのでは。

投稿2019/06/07 19:41

編集2019/06/07 19:48
hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hajifu

2019/06/08 01:09

回答ありがとうございます! 方針のご提示もありがとうございます。 また一つ理解が深まりました。 完全初心者のときに読もうとして難解すぎて投げ出した公式ドキュメント、これを機に読んでみようと思います…!
hayataka2049

2019/06/08 07:53

ドキュメント、端から端まで読む必要はないです。googleやドキュメントサイト内の検索で必要な情報を引っ掛けるスキルの方が大切。
guest

0

listsを逆順([::-1])で参照するとインデックスがずれず、上手くいきますよ。

Python

1lists = [i for i in range(10)] # [0~9までのリスト] 2cnt = 0 3 4for i in lists[::-1]: 5 # 奇数なら削除 6 if i % 2 == 1: 7 lists.remove(i) 8 cnt += 1 9print(lists, cnt) 10 11> [0, 2, 4, 6, 8] 10

追記
偶数のリストを得たいだけなら、以下のようなコードでも出来ます。

Python

1evens_1 = [i for i in range(10) if i % 2 == 0] 2 3lists = [i for i in range(10)] 4evens_2 = lists[::2]

投稿2019/06/07 19:07

編集2019/06/07 19:12
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hayataka2049

2019/06/07 19:50 編集

これで逆順にしてうまくいくことは本質的ではありません。詳細は私の回答に書いたとおりです(listのスライスはコピーを生成します)。
退会済みユーザー

退会済みユーザー

2019/06/07 19:53

知識不足が露呈してお恥ずかしい限りです。 公式ドキュメントの該当箇所を教えてくださって、ありがとうございます。
hajifu

2019/06/08 01:13

回答ありがとうございます! スライスについては今回お二人にご指摘頂いて初めてちゃんと勉強しました。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問