得点による成績わけ

プログラミングの入門本によくある、入力された点数を元に成績を出すプログラムを書く。普通に書けばこんなかんじになる。
プログラム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

Pythonには文字列型とUnicode型がある。

>>> 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()を使っているようだ。以下実験。

PythonPythonを動かす。

>>> import os
>>> py = os.popen("python","w")
>>> print >> py,'print "Hello World!!"'
>>> py.flush()
>>> py.close()
Hello World!!

PythonRubyを動かす。

>>> import os
>>> rb = os.popen("irb","w")
>>> print >> rb, 'print "Hello World!!"'
>>> rb.flush()
>>> print "Hello World!!"
Hello World!!nil

>>> rb.close()

微妙に出力が違う。この違いは何なのだろう?

screenletsのウィジェットを作る

LinuxにはScreenletsというものがある。

便利なのだが、絶対的な数が少ないのが残念。なので、東京電力が公開している雨量・雷観測情報を表示するものを作ってみた。

スキンがアレなのは、センスとGIMPの使い方が分からないせい。
関東の雷の季節は終わったが、新潟は冬が本番(だったはず)。

pythonでホームディレクトリを得る

Pythonのプログラムを書いているのだが、参考にしているソースで実行しているユーザーのホームディレクトリを以下の方法で得ていた。

import commands
home = commands.getoutput("echo $HOME")

何かスッキリしないのでググっていたら次の方法があった。

import os
home = os.environ['HOME']

こっちを使おう。