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

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

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

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

Q&A

解決済

3回答

4420閲覧

python:for文後の処理について、実行されない箇所があるのは何故なのでしょうか?

退会済みユーザー

退会済みユーザー

総合スコア0

Python

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

0グッド

0クリップ

投稿2019/08/11 16:13

pythonで以下のようなファイルを作成しました(不明な点を簡略化するために作成したので、無理矢理感がありますが、その点はご了承下さい)。

python

1import csv 2import time 3 4lists = ["red", "blue", "yellow"] 5for i in lists: 6 csv_file = open('color.csv', 'a+', newline='') 7 writer = csv.writer(csv_file) 8 writer.writerow(i) 9 print(i + "ファイル投入完了") 10 time.sleep(3) 11 12 13fn = 'color.csv' 14 15with open(fn, "r") as f: 16 s = f.read() 17s = s.replace("r,e,d","赤") 18s = s.replace("b,l,u,e","青") 19s = s.replace("y,e,l,l,o,w","黄色") 20 21with open(fn, "w") as f: 22 f.write(s)

上記のファイルは、listsに格納された要素を一つずつ取り出し、csvファイルに格納し、カンマを取るために文字を置き換える処理をしているのですが、何故か最後に取り出した、「yellow」だけが、y,e,l,l,o,wのままです。

csv

123y,e,l,l,o,w 4

私の中ではfor文が完結してから、ファイルの置き換え及び作成が実行されるという考えなのですが、実際にはどのような順序で処理がされているのでしょうか?
また、解決方法があれば教えて頂きたいです。

どなたかアドバイスお願いします。

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

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

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

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

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

guest

回答3

0

openしたものはcloseしましょう。


質問文みたいになる理由の説明。

処理系の実装、OS側でのバッファリングなどに依存する話だし、いろいろな背景知識がないとわからないので、あまり深く考えない方が良いのですが……

まず前提として、ファイルにwriteしたタイミング(csv.writerを介しても同じこと)でファイルへの書き込みが完了する訳ではありません。内部的に都合のいいタイミングで書き込まれるんです。

closeする、あるいは単にflushすることで、そのタイミングで書き出すことをほぼ強制できます(closeしたときにはflushがファイルを閉じる前に呼ばれます。また、closeしたとしてもOS側で行われるバッファリングによってタイミングのズレは生じる可能性があります。拙記事:ファイルオブジェクトのcloseはflushも行う。確実にしたければfsync - 静かなる名辞)。

以上を踏まえて、コードを見ていきましょう。

まずこの部分について。

python

1import csv 2import time 3 4lists = ["red", "blue", "yellow"] 5for i in lists: 6 csv_file = open('color.csv', 'a+', newline='') 7 writer = csv.writer(csv_file) 8 writer.writerow(i) 9 print(i + "ファイル投入完了") 10 time.sleep(3)

csv_file, writerという変数名を毎回使いまわしていますね。ということは、"red"のときと"blue"のときのファイルオブジェクト(型としては_io.TextIOWrapper)csv.writerオブジェクトは他に参照している変数がないので、再代入のタイミングで参照カウントが0になり、メモリ上から消滅します。

といってもいきなり消滅する訳ではなく、オブジェクト固有のメモリ上から消える前に行われる処理があります(いわゆるデストラクタ)。ここでcloseが呼ばれる実装になっているので、このタイミングで書き出されます。

明示的にファイルを閉じなかった場合は、いつかは Python のガベージコレクタがそのファイルオブジェクトを破棄し開かれいていたファイルを閉じますが、しばらくはファイルが開かれたままでいる可能性があります。
7. 入力と出力 — Python 3.7.4 ドキュメント | 7.2. ファイルを読み書きする

で、よくよく考えてみてほしいのですが、"yellow"のときに最後に代入されたファイルオブジェクトcsv.writerオブジェクトは、特に再代入されないので、そのまま残ります。

なので、ファイルは「開きっぱなし」です。そのまま下の部分が実行されます。

python

1fn = 'color.csv' 2 3with open(fn, "r") as f: 4 s = f.read()

この下でprint(s)とかやるとわかりますが、"y,e,l,l,o,w"は書き込まれていません。これはバッファリングされていて、そのうち書き込まれるというものです。この状態で

python

1s = s.replace("r,e,d","赤") 2s = s.replace("b,l,u,e","青") 3s = s.replace("y,e,l,l,o,w","黄色") 4 5with open(fn, "w") as f: 6 f.write(s) 7

が実行されますが、"y,e,l,l,o,w"なんて最初からないので、ファイルの中身は

赤 青

になります。

これですべての処理が終わってPythonプロセスが正常終了するのですが、その前に残っているオブジェクトはすべて破棄され、ここでデストラクタが呼ばれます。「開きっぱなし」にしていたファイルオブジェクトも破棄され、そこでデストラクタが呼ばれるのでやっと"y,e,l,l,o,w"が書き込まれる。

……という流れです。


ついでに。

文字列の書き込みならcsv.writerを使う必要はありません。というか使うだけ無駄です。f.write(string + "\n")とかしてください。

ループが回る度にファイルを開き直すと効率と見た目が悪いです。普通はループの外で開くでしょう。

python

1lst = ["red", "blue", "yellow"] 2filename = "color.csv" 3with open(filename, "w") as f: 4 for x in lst: 5 f.write(x + "\n") 6

書きたいものが赤青黄色なら最初からこの3行を書けば良いので、上の処理は必要ありません。

投稿2019/08/11 17:48

編集2019/08/11 17:49
hayataka2049

総合スコア30933

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

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

退会済みユーザー

退会済みユーザー

2019/08/11 20:47

詳細なアドバイスありがとうございます。 大変勉強になると共に、正直今の自分の力量ではまだまだ理解が足りない部分もあります。 hayataka2049さんのような理論的且つ効率的で無駄の無いコードを書けるよう精進して参りますので、今後もアドバイスよろしくお願いします。
hayataka2049

2019/08/11 21:54

本来は知っておくべきことですが、この辺のことを書いている「Pythonの教材」はほとんどないでしょうから、初心者の方が理解できなくても仕方ありません(バッファリングや参照カウンタをちゃんと説明している入門書がどれだけあることか……)。 ただ、「何言ってるんだこいつ」と思った時点で調べるという努力はしてください。わけわからないキーワードが出てきたら調べて、半可通でもいいからわかるようになれるかどうかが、伸びる人とそうでない人の分かれ目のように思います。
guest

0

ベストアンサー

オープンした後はクローズするクセをつけるのが良いですよ。

python

1for i in lists: 2 csv_file = open('color.csv', 'a+', newline='') 3 writer = csv.writer(csv_file) 4 writer.writerow(i) 5 csv_file.close() 6 print(i + "ファイル投入完了") 7 time.sleep(3) 8

あるいは

python

1for i in lists: 2 with open('color.csv', 'a+', newline='') as csv_file: 3 writer = csv.writer(csv_file) 4 writer.writerow(i) 5 print(i + "ファイル投入完了") 6 time.sleep(3) 7

こちらの方がおすすめです。

投稿2019/08/11 17:07

編集2019/08/11 17:12
AkiraYamamoto

総合スコア30

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

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

退会済みユーザー

退会済みユーザー

2019/08/11 17:13

実例までありがとうございました。 ちょっと気になったのですが、1回1回closeするのではなく、「fn = 'color.csv'」の直前(for文が終了した時点)でcsv_file.close()するのはNGなのでしょうか? また、2つある例文のうち、後者がおすすめなのは前者と比較してどのような理由からなのでしょうか?
AkiraYamamoto

2019/08/11 17:28

今回の例では、最後に close するだけでも動きそうですね。 とはいえ open と close は1セットにするのが無難だと思います。 後者の例ですと、with 句の外に出た時点で自動的に close されるため、 close を書き忘れる心配をしなくてもよくなります。
退会済みユーザー

退会済みユーザー

2019/08/11 17:30

そう言う事だったのですね! とても勉強になりました。 ありがとうございました!
guest

0

書き終わってから読むまでの間に、csv_file.close()しましょう。

投稿2019/08/11 16:42

otn

総合スコア84423

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

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

退会済みユーザー

退会済みユーザー

2019/08/11 17:30

ズバリおっしゃる通りでした! 素早い回答ありがとうございました!
otn

2019/08/11 23:17

後半で、with openが使えているのに前半で使えてないのは、それぞれ別のところからコピペしたものではないかと思います。 他人のコードを参考にするのは良いのですが、「動けば良い」ではなくて「自分が成長する」つもりがあるのであれば、意味を理解するようにしましょう。 前半も、ループごとにopenしていますが、ループの外側で1度だけオープンするのが良いと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問