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

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

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

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

Q&A

4回答

1434閲覧

ある処理をリスト内法表記や高階関数へ書き換えたいがエラーが出る

hello0130

総合スコア6

Python 3.x

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

0グッド

0クリップ

投稿2018/09/07 06:40

編集2018/09/10 21:24

前提・実現したいこと

PyQというプログラミング学習サービスで
for row in f:
if 'good' in path: good_list.append(row)
if 'bad' in path: bad_list.append(row)
上記の処理をリスト内法表記や高階関数を使ったものに書き換えて見たのですが、
good_list, bad_listを参照しようとするとエラーが出てしまいます。

なぜなのか考えて見たのですがさっぱり見当がつきません。
よろしくお願いいたします。

発生している問題・エラーメッセージ

Traceback (most recent call last) File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response) File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app response = self.handle_exception(e) File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb) File "/home/appuser/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise raise value File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app response = self.full_dispatch_request() File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e) File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb) File "/home/appuser/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise raise value File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request rv = self.dispatch_request() File "/home/appuser/venv/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/home/appuser/work/main.py", line 32, in index fortune['message'] = random.choice(good_list) File "/usr/lib/python3.6/random.py", line 260, in choice raise IndexError('Cannot choose from an empty sequence') from None IndexError: Cannot choose from an empty sequence

該当のソースコード

python3

1# 1: 大吉、2: 吉、3: 中吉、4: 小吉、5: 凶、6:大凶 2import random 3from flask import Flask, render_template, request 4 5app = Flask(__name__) 6 7GOOD_FILE = 'input/fortune_good.txt' 8BAD_FILE = 'input/fortune_bad.txt' 9 10 11@app.route("/") 12def index(): 13 good_list = [] 14 bad_list = [] 15 for path in [GOOD_FILE, BAD_FILE]: 16 with open(path, encoding='utf-8') as f: 17 #うまく動く 18 for row in f: 19 if 'good' in path: good_list.append(row) 20 if 'bad' in path: bad_list.append(row) 21 22 #fortune_list = list(f) 23 24 #リスト内法表記 エラー発生 25 #good_list = [row for row in fortune_list if 'good' in path] 26 #bad_list = [row for row in fortune_list if 'bad' in path] 27 28 #高階関数へ書き換え エラー発生 29 #good_list = list(filter(lambda x: 'good' in path, fortune_list)) 30 #bad_list = list(filter(lambda x: 'bad' in path, fortune_list)) 31 32 #うまく動く 33 #good_list.extend([row for row in fortune_list if 'good' in path]) 34 #bad_list.extend([row for row in fortune_list if 'bad' in path]) 35 fortune = {} 36 if request.args.get('fortune', ''): 37 fortune['no'] = random.randint(1, 6) 38 if fortune['no'] <= 4: 39 fortune['message'] = random.choice(good_list) 40 if fortune['no'] >= 5: 41 fortune['message'] = random.choice(bad_list) 42 return render_template('index.html', fortune=fortune) 43

試したこと

補足情報(FW/ツールのバージョンなど)

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

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

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

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

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

quickquip

2018/09/07 06:50 編集

ipythonを使ってませんか? ←すみません、勘違いでした。お気になさらずに
hello0130

2018/09/07 06:55

返信ありがとうございます。ipythonは使っていないのですが、PyQというプログラミング学習サービス上で発生しています。あまり関係ないと思っていましたが、書き換え方に問題がないのであればPyQ固有の問題なのかもしれません。
fuzzball

2018/09/07 07:16

randomが吐いている例外のようなので、質問のコードは関係ないのでは?(正しく動くかどうかは別にして) テストデータ生成にrandom使ってるとか。
hello0130

2018/09/07 08:06

回答ありがとうございます。コードを変更するとエラーがでるのでそこが原因だと考えました。エラーメッセージの内容はlistが空だと言っていると理解していますがあっているのでしょうか?エラーメッセージをいまいち理解できていません。エラーコード全文を追記させていただきましたので見ていただけたらと思います。
fuzzball

2018/09/07 08:10

このエラーってリスト内法表記だけじゃないですか?高階関数でも出ます?
hello0130

2018/09/07 08:30

返信ありがとうございます。PyQには判定ボタンがありコードをテストして確認しているようです。高階関数の場合はたまに合格判定になり、リスト内法表記だと何回押しても合格判定にはなりません。どういうことでしょうか?
quickquip

2018/09/07 12:23

fortune['message'] = random.choice(good_list) は自分で書いたコードですか? 書いてないコードですか?
hello0130

2018/09/07 12:59

返信ありがとうございます。その部分は自分で書いた部分です。その部分に適当なリストを入れてみたところエラーが出なくなったのでgood_list自体に問題があるのではと考えています。
guest

回答4

0

fortune['message'] = random.choice(good_list)

というコードを書いていてそれがエラーになっているのに、なぜそれを質問に含めなかったんでしょうか。
最初から原因はこれで、潜在的なバグがここにあります。

https://docs.python.jp/3.6/library/random.html#random.choice

seq が空のときは、 IndexError が送出されます。

と明確に書いてあります。

good_listが空な状態でここに到達するとエラーになるのです。推測ですが、bad_listも同様のコードを書いているのではありませんか?

質問のソース、リスト内法表記とコメントされている部分と、高階関数ととコメントされている部分には明確にバグがあってbad_listが空になります。
そのせいでエラーが発生しました。
(で、これは修正されたと思います)

もう一つ、PyQが準備しているテストデータ群の中か、あるいはランダム生成されるテストデータか、good_listbad_listが空になるようなエッジケースでエラーになっていたのではないでしょうか?

そのようなテストデータをたまたま踏むとエラーになるわけです。
現実にもよくあるエッジケースでだけ失敗するパターンですね。

投稿2018/09/08 01:22

quickquip

総合スコア11038

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

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

hello0130

2018/09/08 11:32

返信ありがとうございます。お付き合いいただきとてもありがたいです。 質問するにあたって何を書けば適切なのかが分かっていないようです。 自分で判断せずにわかっていることをすべて書くようにしようと思います。 bad_listも同様なコードを書いています。ランダムな数字を基にして、 それを判定してfortune['message'] にgood_list,bad_listどちらかが入るようになっているのですが、 good_listの場合のみエラーが出ます。bad_listが選択された場合は予定通りの動きをしています。 # 元の処理の場合は全くエラーが出ないので別の原因なのではないかと考えます。 まだ何がどうなっているか理解しきれていないので、いろいろ考えて試して理解を深めようと思います。
quickquip

2018/09/08 12:09

> good_listの場合のみエラーが出ます。 bad_listが空になるようなデータをテストしてみてくださいよ。
hello0130

2018/09/09 09:40 編集

返信ありがとうございます。 元の処理のif 'bad' in path: bad_list.append(row)をコメントアウトさせたところ、 予想通りエラーメッセージにfortune['message'] = random.choice(bad_list)が出ました。 自分が思っていることとしては以下が発生しているので、 どういうことだろうか?となっています。 # エラー発生 good_list.extend([row for row in fortune_list if 'good' in path]) bad_list = ['a', 'b'] # エラー出ない good_list = ['a', 'b'] bad_list.extend([row for row in fortune_list if 'bad' in path])
quickquip

2018/09/09 09:43

random.choice のドキュメントのリンクを貼ったはずですが
hello0130

2018/09/09 10:00

返信ありがとうございます。 random.choiceに空のリストを入れればエラーが出るのは理解しています。 なぜgood_listが空になるのかわからない。ということです。
quickquip

2018/09/09 11:00

goodと書かれていないデータを与えたら空になりますよね? 最低1件は入っている保証はありますか? 極論、空ファイルを与えるだけでエラーです。
quickquip

2018/09/09 11:29

質問を引用するのは非常識な話なのでしょうがないですが、こちらでできるのは、空になって当然のコードでかつ空になったらエラーになるコードなので空になってエラーになったのでしょう、という指摘までですよ。
hello0130

2018/09/10 09:27

返信ありがとうございます。good,badそれぞれが含まれたデータが必ず存在します。
quickquip

2018/09/10 09:52 編集

goodが入ったファイル名から読んだデータはgood_listに、badが入ったファイル名から読んだデータはbad_listに格納しているのでしょうけど、ファイルの存在と、ファイルが空でないことを確認しているということですか? 合格判定という言葉が出ていることから、プログラムコンテストの要領になっていると想像しました。 PyQ側にある種のテストデータが複数パターン用意されていて、それらを全部、あるいは必須セット+ランダムにいくつかとかテストされていると思ってました。 そうではないのですか? もしくは 用意されているテストデータは回答者側は確認できるのでしょうか? 「good,badそれぞれが含まれたデータが必ず存在します」と断言できるということは、エラーが起きた時にどういうデータで発生するか把握できているということですか?
quickquip

2018/09/10 09:52

どういうデータで発生するか把握できているなら、「なぜgood_listが空になるのかわからない」なんてことがありえるんでしょうか? 「なぜgood_listが空になるのかわからない」と言っていることから、どういうデータで発生するか把握できていない(把握できないシステムを用いている)という仮定で回答しているのですが。
hello0130

2018/09/10 21:26

返信ありがとうございます。 >どういうデータで発生するか把握できているなら、「なぜgood_listが空になるのかわからない」なんてことがありえるんでしょうか? 自分の認識ではそれが起きています。 初めからすべて書くのが適切だったということを学ばせていただきましたが、 おみくじを表示するWebアプリケーションをつくろう。というものです。 該当のソースコードを変更したので、もしよろしければ見ていただければと思います。
quickquip

2018/09/10 23:44 編集

「うまく動く」方は**必ず**うまく動くし、「エラー発生」は**必ず**エラーが発生するのが現状で、"たまに合格判定が出るようになりました"は古い情報or間違いということでいいですか? ソースからは当然そうなると思いますが念のため。
hello0130

2018/09/11 13:36

返信ありがとうございます。 「うまく動く方」は必ずうまく動きます。bad_list,good_lisetどちらが選択された場合にも狙い通りの表示がされます。 「エラー発生」の方はgood_listが選択された場合のみエラーが発生します。 実際に動かして確認できるのですがbad_listが選択された場合のみ狙った結果が表示されています。
guest

0

リスト内表記のコードは、こちらでは正常に動作しなかったので、とりあえずそれを修正しておきます。

python

1# リスト内法表記 2with open(path, encoding='utf-8') as f: 3 good_list = [row for row in f if 'good' in path] 4 f.seek(0) #追加 5 bad_list = [row for row in f if 'bad' in path]

【追記】

処理時間とかは分かりませんが、こっちの方が気持ち悪くないかも。

python

1# リスト内法表記 2with open(path, encoding='utf-8') as f: 3 rows = list(f) #リスト化 4 good_list = [row for row in rows if 'good' in path] 5 bad_list = [row for row in rows if 'bad' in path]

投稿2018/09/07 08:45

編集2018/09/07 09:11
fuzzball

総合スコア16731

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

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

hello0130

2018/09/07 09:07

回答ありがとうございます。補足情報からの続きです。まったく合格判定が出なかったものがたまに合格判定が出るようになりました。 インタープリンタでは部分的にしか試していなかったので気づかなかったようです。 good_listでファイルのポインタの位置が最後尾になるのでポインタを0に戻す必要があると理解しました。
fuzzball

2018/09/07 09:15

>>ポインタを0に戻す必要がある はい、その通りです。 で、エラーは出なくなったのでしょうか? 質問は「なぜエラーが出るのか?」であって、「なぜ不合格になるのか?」ではないと思いますが。
hello0130

2018/09/07 09:26

返信ありがとうございます。追記の場合の方がすっきりするように感じます。 合格以外の場合は同じくIndexError: Cannot choose from an empty sequenceとエラーが出ます。
fuzzball

2018/09/07 09:38

仕様の漏れとかはないでしょうか?例えば「ファイルが存在しないとき」とか。 エラーの頻度など分からないので何とも言えませんが、特殊な条件のときにエラーが出ているような気がするようなしないような。 特殊な条件ではなく、単純な仕様漏れとか、仕様の誤認とか。
hello0130

2018/09/07 10:42

返信ありがとうございます。ちょっと頭の中がごちゃごちゃしているので時間をかけて考えてみようと思います。 必要ないかもしれませんが何かしら進展があった報告させていただきます。 お付き合いいただきありがとうございました。とてもありがたかったです。
hello0130

2018/09/07 12:56 編集

教えていただいたことをもとに、下記のようにしたところリスト内法表記ではエラーが出なくなりました。 fortune_list = list(f) good_list.extend([row for row in fortune_list if 'good' in path]) bad_list.extend([row for row in fortune_list if 'bad' in path]) さらに、extendを使わない内法表記・高階関数ともに good_list の if 'good' in pathの部分をTrueにしてみたところ、 欲しい結果は得られませんがエラーが全くでなくなりました。 なので if 'good' in path の部分が原因なのではないかと考えていますが、なぜなのかは全く分かりません。
guest

0

f.readlines()をlist(f)にしてみるとか。

投稿2018/09/07 07:27

hayataka2049

総合スコア30933

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

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

hello0130

2018/09/07 08:16

回答ありがとうございます。試してみましたがエラーが出てしまいました。 同じ処理をしていると理解しているのですが合っているのでしょうか? さらにそうすることによってエラーがなくなると考えられた意図は何でしょうか? 言葉がとげとげしい気がしますが、そういう意図はありません。単純に知りたいんです。 よろしくお願いいたします。
hayataka2049

2018/09/07 08:23

基本的に同じ結果になる処理ですが、相違箇所はそこかなぁ、と。
hayataka2049

2018/09/07 08:25 編集

いま思い付いたのですが good_list = [row for row in f if 'good' in path] を good_list.extend([row for row in f if 'good' in path]) みたいにするとどうです?(badのほうも)
hello0130

2018/09/07 12:45 編集

返信ありがとうございます。 教えていただいたことをもとに、下記のようにしたところエラーが出なくなりました。 なぜうまくいくのでしょうか?もしよろしければ教えていただければと思います。 fortune_list = list(f) good_list.extend([row for row in fortune_list if 'good' in path]) bad_list.extend([row for row in fortune_list if 'bad' in path])
hayataka2049

2018/09/07 13:08

good_list.extend([row for row in fortune_list if 'good' in path]) を思いついたのは、オブジェクトの同一性絡みかな? と考えたからです。 「# 元の処理」のコードではいずこかで宣言されたgood_list, bad_listに要素が追加されますが、内包表記、filterはともに新しいリストオブジェクトを作ります。 PyQのシステムを知らないのですが、動かし方によってはオブジェクトの同一性が絡んでいるのかなぁ、と。 ただ、fuzzballさんの回答へのコメントで条件式を常にTrueになるようにしたらエラーが出くなったというのはこれでは説明できないので、違うのかも。
hello0130

2018/09/07 14:15 編集

返信ありがとうございます。何度も質問に答えていただきとてもありがたいです。 オブジェクトの同一性とはid()が同じかどうかだと、なんとなくですがそう理解しています。 試しにgood_list = [],bad_list = []をコメントアウトさせてみましたが同じエラーが出ました。 [row for row in fortune_list if 'bad' in path]は結果を確認するとうまく動作しているのですが [row for row in fortune_list if 'good' in path]だけがなぜかうまく動いてくれません。 なので、もう本当に謎です。
hayataka2049

2018/09/09 11:39

ふと思ったのですが、'good' in rowじゃなくて'good' in pathなんですね コードや問題の全貌がわかりませんが、'good'ないし'bad'のどちらかだけpathに含まれているなら、片方のリストにすべて入る動作になるのでは 元のコードでもそれは同じことですが・・・
hello0130

2018/09/10 09:36

返信ありがとうございます。good,badそれぞれが含まれたデータが必ず存在します。 何か根本的に勘違いしているのかもしれません。考えていただきありがとうございます。
guest

0

インタープリンタで試してみたところ書き換えたコードは問題なく動きました。なので、PyQ固有の問題という結論になりました。
もし何かこういう事じゃないか?というのがあれば教えていただければと思います。ありがとうございました。

投稿2018/09/07 07:15

hello0130

総合スコア6

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問