得点による成績わけ
プログラミングの入門本によくある、入力された点数を元に成績を出すプログラムを書く。普通に書けばこんなかんじになる。
プログラムI
#!/usr/bin/env python score = 90 if 0 <= score < 40: print "you are D." elif 40 <= score < 60: print "you are C." elif 60 <= score < 80: print "you are B." elif 80 <= score <= 100: print "you are A." else: print "Erro: Your score is %d. Why???" % score
なにも知らないと条件に、40 <= score and score < 60 と書きがちだが、40 <= score < 60 とより自然に書ける。
もう一つの方法として以下の様な書き方ができる。
プログラムII
#!/usr/bin/env python D = range(0,40) C = range(40,60) B = range(60,80) A = range(80,101) score = 50 if score in D: print "you are D" elif score in C: print "you are C" elif score in B: print "you are B" elif score in A: print "you are A" else: print "your score is %d. Why???" % score
ココのソースを読んでいて知った方法なのだが、「scoreがDにあれば」と言うように読むことができる。ある意味、より自然言語っぽい。しかし、実行コストはどう考えても不利。timeitモジュールを用いて実行速度を計測した結果。
score=0の場合 | |
---|---|
プログラムI | 0.5548861026763916 |
プログラムII | 0.47245001792907715 |
score=2の場合 | |
---|---|
プログラムI | 0.54223418235778809 |
プログラムII | 0.57019996643066406 |
score=100の場合 | |
---|---|
プログラムI | 1.1403129100799561 |
プログラムII | 6.2671980857849121 |
score=0の時こそプログラムIIの方が速いが、score=2あたりで逆転される。score=100の時には5~6倍の差となる。
可読性は上がるので使いどころを間違わなければ有効な書き方かなと思った。
Pythonで日本語
Pythonで日本語を扱うには、Unicode型で扱うのが楽な様だ。
参考 PyJUG 日本語環境でのPython
>>> Hokkaido = "北海道" >>> print Hokkaido 北海道 >>> type(Hokkaido) <type 'str'> >>> Hokkaido '\xe5\x8c\x97\xe6\xb5\xb7\xe9\x81\x93' >>> len(Hokkaido) 9 >>> uHokkaido = u'北海道' >>> print uHokkaido 北海道 >>> type(uHokkaido) <type 'unicode'> >>> uHokkaido u'\u5317\u6d77\u9053' >>> len(uHokkaido) 3
ファイルからテキストを読み込む時は、codecsを使って次の様に出来る。
>>> import codecs >>> f = codecs.open("jpn.txt",'r','utf_8') >>> txt = f.readlines() >>> type(txt[0]) <type 'unicode'> >>> for t in txt: ... print t, ... test 日本語てすと!!! test
Webからテキストを読み出すときはcodecs.getreader()を使えば良さそう。
>>> import urllib >>> import codecs >>> url = "http://live24.2ch.net/eq/subject.txt" >>> f = urllib.urlopen(url) >>> f = codecs.getreader('shift_jis')(f) >>> subjects = f.readlines() >>> type(subjects[0]) <type 'unicode'> >>> for sbj in subjects: ... print sbj, ... 1221234583.dat<>ジョセリーヌのやつでさ (158) 1219554767.dat<>精進湖の水位を生暖かく見守るスレ6【まったりと】 (184) 1221805102.dat<>海の異常&動物の大移動を取り上げるスレ (13) 1220349531.dat<>今井真人の地震予知研究会13 (390) 1220050438.dat<>地震、雷、火事、地震 (65) 1221835586.dat<>台風13号の進路、プレートの境界線をなぞってる(汗) (35) :
Pythonのenumerateでハマった
Pythonではリストなどにループをかける場合、普通にこんな感じで書くわけだ。
>>> a = [0,1,2,3,4,5] >>> for x in a: ... print x, ... 0 1 2 3 4 5
インデックスが必要な場合はenumerateを使う。
>>> a = [0,1,2,3,4,5] >>> for i,x in enumerate(a): ... print "a[%d]=%d"%(i,x) ... a[0]=0 a[1]=1 a[2]=2 a[3]=3 a[4]=4 a[5]=5
これを用いて一つ前の要素との差を求めたかった。a[1]とa[0]の差は1といった感じで、a=[0,1,1,1,1,1]になって欲しい。0番目は何もしない。この何もしないというのを、リストのインデックス1番目から始めれば良いと考えたのがマズかった。
>>> a = [0,1,2,3,4,5] >>> b = [x for x in a] >>> for i,x in enumerate(a[1:]): ... a[i] -= b[i-1] ... >>> a [-5, 1, 1, 1, 1, 5]
期待する値ではない。考えれば当然だがa[1:]というのはリスト[1,2,3,4,5]をである。iにも0から入っていく。これをaのインデックス1から渡しているので、iには1から入っていくと思い込んでいた。結構ハマってしまった。書き直すとすればif文で条件を分けるか、iに1を加えるか。if文の方がいいかな。
>>> a = [0,1,2,3,4,5] >>> b = [x for x in a] >>> for i,x in enumerate(a): ... if i != 0: ... a[i] -= b[i-1] ... >>> a [0, 1, 1, 1, 1, 1]
ファイルから読み込んでディクショナリに
20080911000200 2 20080911000300 6 20080911000401 10 20080911000500 11 20080911000600 12 20080911000700 16 :
こんな感じに記述されたテキストファイルがある。実際にはWebにあるので以下のように読み込む。
In [1]: import urllib In [2]: url = "http://stats.2ch.net/kawasemi-m/eq-20080911.txt" In [3]: f = urllib.urlopen(url) In [4]: enagy = [e for e in f.readlines()] In [5]: enagy ['20080911000200\t2\n', '20080911000300\t6\n', '20080911000401\t10\n', '20080911000500\t11\n', : '20080911235600\t2602\n', '20080911235701\t2603\n', '20080911235800\t2604\n', '20080911235900\t2606\n']
'\t'までの部分は西暦+月+日+時+分+秒の並びになっているが、これの秒の部分を除いたものをkeyとして'\t'以降を要素とするディクショナリと作りたい。
keyの部分split()で得て、後半2つをスライス。
In [16]: e = enagy[0] In [19]: e.split('\t')[0][:-2] Out[19]: '200809110002'
keyの文字数は決まっているので直接スライス。
In [20]: e[:12] Out[20]: '200809110002'
後者の方が直感的かな?
問題は要素の部分。split()を駆使して
In [24]: e.split('\t')[1].split('\n')[0] Out[24]: '2'
とすれば得られなくも無い。最後の1文字をスライス。
In [27]: e.split('\t')[1][:-1] Out[27]: '2'
どう考えても後者の方が自然だな。
というわけで、こんな感じでディクショナリを作ってみた。
import urllib enagy={} url = "http://stats.2ch.net/kawasemi-m/eq-20080911.txt" f = urllib.urlopen(url) tmp = [e for e in f.readlines()] for e in tmp: enagy[e[:12]] = int(e.split('\t')[1][:-1])
Pythonで外部プログラムを動かす
id:y_yanbeさんのコメントで、任意のコマンドの出力結果をcommands.getoutput()で得られることに気が付いた。今までコマンドの出力結果を得るというプログラムを書いたことがなかったので、てっきりos.system()で得られるのだと思っていた。返ってくるのはプロセスの終了ステータスなんですね。。。
commands.getoutput()で標準出力を得られることは分かった。では、標準入力からの入力を得るプログラムを動かすにはどうするか。
Python Cookbookに求めるものが書いてあった。os.popen()を使えばいいらしい。そもそもcommands.getoutput()はos.popen()を使っているようだ。以下実験。
>>> import os >>> py = os.popen("python","w") >>> print >> py,'print "Hello World!!"' >>> py.flush() >>> py.close() Hello World!!
>>> import os >>> rb = os.popen("irb","w") >>> print >> rb, 'print "Hello World!!"' >>> rb.flush() >>> print "Hello World!!" Hello World!!nil >>> rb.close()
微妙に出力が違う。この違いは何なのだろう?