uayeb

uayeb

uayebはマヤ暦の「無名月」

uayeb RSS Feed
 
 
 
 

Python楽しい

昨年秋から、プログラミング言語のPythonにハマっていて、かなり業務にも役に立っています。

いろいろ組ながら覚えている最中ですが、よりによって年末最終日(12/27)に、非常に悪質なバグというか誤解に基づくトラブルに引っかかったので、参考までに記しておきます。

for line in … の意味を誤解していた件。

Pythonでは、

f = open(“filename.txt”)

として、テキストファイル filename.txt をオープンしておくと、

for line in f:

print line

で、テキストとしての行(改行で区切り)を1行ずつ読出すことができるのです。

それで、だいぶいろんな処理を書いていました。

さて、年末のコードは、ユーザーに複数行の「キーワード」を入力させ、データベース内の合致するものを検索する、という内容でした。

ウィンドウを出して、ユーザーにGUI形式で複数行のテキストを入力させ、「完了」ボタンを押すと、テキスト全体が文字列変数s に帰ってくるようにしました。

sの内容は問題ありません。print s とすると、

キーワード1

キーワード2

キーワード3

と表示されます。改行で区切られていることもわかります。

そこで、キーワードを1つずつ、配列変数keyword[] に入れようと思いました。

keyword = [] #配列(pythonではリストといいます)変数ですよ、という宣言

for line in s:

keyword.append(line)

で切り出せると思ったのですが、なぜかキーワードに何もヒットしません。

おっかしいな、ちゃんとkeywordに入ってるよね? と思って中身を見てみると、

なぜかkeyword変数の中身は、半角カタカナや記号が1字づつ入っているのでした。

おかしい。今まで改行区切りのテキストファイルに対して、まったく問題なく動いていた処理が、どうして今回動かないのだ?

for line in … の意味を、私は何か決定的に誤解していたのだろうか? と、愕然としたのでした。

それで、Pythonのインタプリタ環境で、 文字列内容をfor line in … で読ませてみて、想定外の事態に気がつきました。

“abcde[改行]12345[改行]”   という文字列 s に対して、for line in s: で切り出しを行なうと、

a

b

c

d

e

[改行]

1

2

3

4

5

[改行]

という形で切り出しが行なわれるのです。切り出しは「1バイト単位」なので、前に変な半角カナが出たのは、シフトJISコードが上位と下位に分割されていたからなのでした。

……しかし、テキストファイルの読出しなら、ちゃんと行単位で切り出されるのに、なぜ文字列が相手だと1バイト単位になるのだろう?

謎に思っていましたが、あるところで、こんなコードを見て謎が解けました。

for data in [2,4,11,13,18]:

処理……

そうなのでした。for line in … というコードを見ると、これがテキストから「行」を切り出すようなループを提供する書式のように思ってしまいますが、考えてみたらfor lineである必要は本当はなく(慣用的にそう書くことが多いだけ)、for gyou   でも、for ligne  でも、同じように動くのです。

では、for は何をしているのか。

for はループを作る際の予約語で、典型的な用法は、

for i in range(10):

みたいに使います。この例だと、iには0から9までが順に入ります。range()関数は、0から引数より小さい数字すべてを返すものです。

では、range(10)の正体はなにか。インタプリタ上で print range(10) とするとわかります。それは……

[0,1,2,3,4,5,6,7,8,9]

でした。そう、range関数は、こういう配列(リスト)を返すのです。だから、上で見た、for data in [2,4,11,13,18] と、形は全く同じです。for は、inの後ろで指定されたリストの中身を順に取り出す、という動きをするのでした。

では、なぜテキストファイルに対してfor を使うと行が切り出されるのでしょうか?

そこで、f = open(“finename.txt”) で得たfを表示させてみたら……

[行1,行2,行3…]

となっていたのでした。テキストファイルをオープンしたときのf は、文字列ではなくて、文字列のリストだったのです。

では、文字列に対してforを使ってアクセスすると、どうなるのでしょう?

そういえば! Pythonの文字列は、+で連結したり、検索、置換などの「メソッド」が使えるだけでなく、リストとしても扱えるのでした。たとえば、

s = “abcde”

と定義すると、

print s[0]

で、0番目の要素(0から数える)であるaが得られます。

s[0] = “f”

とすれば、文字列sの内容は

“fbcde”

になります。

このように、文字列は、1文字(1バイト)ごとに区切られたリストでもあるのです。

ですから、forでアクセスすると、1バイトごとに切り出されてしまうのです。

でもって、今回は結局、改行単位で切り分けられればいいので、forループは回さず、

keyword = s.split(“\n”)

ですませました。.splitは「分割」のメソッド。\nは改行コード。文字列sを改行コードで区切り、結果をリストとして返します。

さて、これで無事キーワードが切り出せたと思ったのですが、次にはさらに不可解な動作が待っていたのでした……(次回につづく)

Leave a Reply

  • 711769 Visitors

  • twitter

    Pages

    カテゴリー

    メタ情報