応用編

select()の話はこちら

.py って拡張子がついてないファイルを import する

Python の import では ".py" って拡張子が付いたファイルしか読み込めませんが、 imp.load_source() を使うと、どんなファイル名でも python として読み込めます。
import os
import imp

base = os.path.dirname(os.path.abspath( __file__ ))
my_path = os.path.join(base, "my_python_filename");
my_module = imp.load_source("my_name", my_path);
my_module.hoge()
普通は相対パスで読み込むと思うけど、単に load_source() するだけだと、 ユーザが実行する際のカレントディレクトリからのパスになってしまい、意味がない。 そこで、ファイルが置かれている場所のパス名を取得して、つなげている。

ディレクトリ内のファイルをまとめて、文字コード変換する

はじめ手作業でやってたら、サブディレクトリが大量にあることがわかったため、 python の os.walk を使うことに。
import os
for base,dirs,files in os.walk("."):
    for f in files:
        target = os.path.join(base, f);
        if target.endswith(".pm") or target.endswith(".cgi"):
            print 'mv %(x)s %(x)s.temp; nkf -e %(x)s.temp > %(x)s'%{"x":target}
            os.system('mv %(x)s %(x)s.temp; nkf -e %(x)s.temp > %(x)s'%{"x":target})

1024 みたいに、2 の n 乗が 10 の m 乗に近いことってあるのか?

調べてみるために、ざっと書いてみた。
t = 1
for i in range(1000):
   t *= 2
   d = t * 1.0
   while d > 1:
     d *= 0.1
   if d < 0.1025: print t
結果、始めに 10 と続く数でさえ、10141204801825835211973625643008 まで待たないといけない。
1024 が 1000 にすごく近いのは偶然なんだなー。

INT_MAXとかFLOAT_MAXとか、整数の最大値や浮動小数点数の最大値を取得

numpyという数値処理モジュールの中だけど、多分普通の型でも同じ。
from numpy import finfo, float_
finfo(float_).max

サイズを指定してリストを作る

大きさを明示的に指定して配列を作る方法はいくつか考えられるが、 [0] * 100000みたいなのが一番速いと思う。 (mmapオブジェクトを使う方法も考えられるけど) 試しにrange()を使う方法、ひとつずつappendする方法と時間をtimeコマンドで比較してみた。
# A = [0] * 1000000
0.028us (マイクロ秒)

# A = range(1000000)
0.068us (マイクロ秒)

# A=[]; for i in range(1000000): A.append(0)
0.248us (マイクロ秒)
ひとつずつappendに比べて、このサイズだと10倍速いですね。
ついでにCとも比べてみた。
# (Cで)
  A = (int *)malloc(sizeof(int) * 1000000);
  for(i = 0; i < SIZE; i++) A[i] = 0;
0.008us (マイクロ秒)
さすがにCには余裕で負ける...

日時フォーマットを指定

t = time.time()
t_str = time.strftime("%Y-%m-%d_%H-%M-%S", t)

新しいファイルをサイズを指定して作る

mmapしてランダムにファイルに書き込むのに、まっさらなファイルをopenして適当なサイズを指定してmmap()したらおこられてしまった。
あらかじめ領域を確保したいだけだから、例えば0をサイズ分だけ書き込むのはためらわれる。 (ディスクアクセスが発生していまうから。ほんとはinodeだけ書き換えればいいはず) ちょっと考えたら、open()したあとseek()すればいいみたい。
    fp = open(filename, "w")
    fp.seek(size)
でもこれだけだとサイズは変わらないみたい。そこで、seek()した先に1バイトだけ書き込んでみる。
def create_empty_file(filename, size):
    fp = open(filename, "w")
    fp.seek(size-1)
    fp.write('\0')
    fp.close()
これで、任意のサイズのファイルを簡単に作る関数ができた。

Pythonでmmap()

あらかじめ/tmp/hogehogeに20バイト以上なんか書いておきましょう。
import mmap, os

def mmap_file(fn):
    fd = open(fn, "r")
    size = os.path.getsize(fn)
    data = mmap.mmap(fd.fileno(), size, access=mmap.ACCESS_READ)
    print data[10:20] # ファイルの10-19バイト目を表示する

mmap_file("/tmp/hogehoge")

変数が定義されているか調べる

vars()は今使える変数のリストを返してくれる。 (dir()でもほぼ同じ結果が返る) だから、ある変数aが定義されているかどうか知りたかったら、
a in vars()
とすればよい。
また、
del a
とすると、aという変数を消すことが出来る。

エラーをtry...excepでキャッチした後で表示

Thanks to kennyくん
def getStackTrace(e):
    '''
    get the stacktrace of an immediately
    previous exception as a string
    '''
    err_str = cStringIO.StringIO()
    traceback.print_exception(e.__class__, e, sys.exc_info()[2], None, err_str)
    return err_str.getvalue()

Pylint

import pylint
で、 Pythonでoption explicitみたく文法チェックしてくれる。

文字列をリストに変換

文字列を一文字ごとのリストに変換するには、list()で囲むだけ。
s = "abcdef"
print list(s)
結果
['a', 'b', 'c', 'd', 'e', 'f']

16進数と10進数

16進数(の文字列)から10進数への変換。 例えば"1a"→26みたいな。
int()とかを調べたけどかっこいい方法が見つからなかったので、遅そうだけどevalを使いました。
str = "1a"
i = eval("0X" + str)
print i

enumerateを使おう!

Pythonのfor文を使うと、リストに関する処理がとてもしやすいが、ループ変数(iみたいなの)に相当するものが無いので ちょっと不便な場合もある。例えば、
A = ["apple", "banana", "carrot"]
for a in A:
  print a
とすると順番にapple, banana, carrotが表示されるが、その前に1, 2, 3と番号が振りたい(1 apple, 2 banana, 3 carrotみたいに)とき、普通に考えると順番に増える変数を作る
A = ["apple", "banana", "carrot"]
i = 0
for a in A:
  print i, a
  i += 1
か、ループ変数自体を変える
A = ["apple", "banana", "carrot"]
for i in range(len(A)):
  print i, A[i]
だけど、どちらもいまいちすっきりしない。
そこでenumerateという関数を使うと、すっきり片付く。
A = ["apple", "banana", "carrot"]
for i,a in enumerate(A):
  print i, a
元々ループ変数が複数あるときは、パターンマッチ的な記述をするとうまくいく。
A = [(1,2), (3,4)]
for i,(x,y) in enumerate(A):
    print i, x, y

浅いコピーと深いコピー。

copy.copyでjavaのcloneみたいにオブジェクトのコピーが出来るけど、 普通にオブジェクトをコピーしても、メンバ変数に入っているオブジェクトや配列(これもオブジェクト)はコピーされない。 copy.deepcopyを使うと、変数に入っているオブジェクトも「可能な限り」コピーしてくれる。
import copy

class A:
    def __init__(self):
        self.array = [1,2,3]
        self.string = "abc"

    def __str__(self):
        ret ="{array : ["
        for a in self.array:
            ret += "%d "%(a)
        ret += "], string:%s}"%self.string
        return ret

a = A()
copied_a = copy.copy(a)
deepcopied_a = copy.deepcopy(a)
a.array[2] = 5
a.string = "hello"
print "a            = ", a
print "copied_a     = ", copied_a
print "deepcopied_a = ", deepcopied_a
実行結果。
a            =  {array : [1 2 5 ], string:hello}
copied_a     =  {array : [1 2 5 ], string:abc}
deepcopied_a =  {array : [1 2 3 ], string:abc}
a.arrayやa.stringを書き換えるとcopied_a.arrayやcopied_a.stringも書き換わってしまいますが、deepcopied_a.arrayや deepcopied_a.stringは別なものになっているので書き換わりません。

文字と文字コードの変換

バイトからasciiコード(int)を返す ... ord()
asciiコード(int)から文字を返す ... chr()

glob (ファイルの列挙)

ある条件にあうファイルを全て列挙する。 ディレクトリを開く(opendir)代わりにも使える。
import glob

for f in glob.glob('/etc/*.conf'):
    print f
こうすると、条件にマッチするファイルへのフルパスがfに順次入ります。
/etc/pam.conf
/etc/ypserv.conf
/etc/host.conf
/etc/cvs-cron.conf
...

popenを使って、子プロセスのpidを取得

import popen2
import os

p = popen2.Popen3("command_hoge", True) 
# Trueを付けると、エラー出力と標準出力を分けて使える

print p.pid
# p.pidに"command_hoge"を実行しているプロセスIDが入っている

p.tochild.write("moi")
# command_hogeの標準入力にmoiと書く

p.fromchild.read(100)
# command_hogeの標準出力を100バイト読む

p.childerr.read(100)
# command_hogeのエラー出力を100バイト読む

os.kill(p.pid, 9)
# popenしているプロセスをkillする

popenしたプロセスは、正しくpcloseしなければいけません。 …といっても、Pythonにpcloseはありません。かわりに全てのファイルディスクリプタを閉じて、wait()を呼びます。
import popen2

p = popen2.Popen3("bash", True) 

# 相手(popenされたプログラム)にとってのstdinを閉じる (EOFを送る)
p.tochild.close()

# 相手(popenされたプログラム)が何も出力していなければ、
# 閉じなくてもブロックしないかもしれないけど、
# 一応礼儀として閉じましょう。
p.fromchild.close()

# こっちも。
p.childerr.close()

# これでdefunctになった子プロセスを回収する
p.wait()

リストのランダムな置換(0,1,2,3)から(2,0,3,1)など)を作る。

import random
def getOrdering(n):
    A = []
    B = range(n)
    for i in range(n):
        index = int(random.random() * (n-i))
        A.append(B.pop(index))
    return A

getOrdering(10)
  • リストからランダムに重なり無くn個取り出す。 (リストの方が短いときにはリストをランダムに置換したのを返す)
    import random
    def pickSome(list, n):
        retlist = []
        picklist = list[:]
        imax = min(n,len(picklist))
        for i in range(imax):
            index = int(random.random() * len(picklist))
            retlist.append(picklist.pop(index))
        return retlist
    
    使い方。
    A = range(7)
    print pickSome(A, 10)
    
  • ソート

    リスト(配列)のソート。昇順(小さい順)になります。
    >>> A=[3, 4, 2, 0, 5]
    >>> A.sort(lambda x,y: cmp(x, y))
    >>> A
    [0, 2, 3, 4, 5]
    

    mapを使う。

    A= ["1", "2", "-1"]というリストを、全て数値に変換する
    A= ["1", "2", "-1"]
    B = map(string.atoi, A)
    
    これでB = [1, 2, -1]になります。
    lambdaだってこわくない。Bの各要素を2乗してみます。
    C = map(lambda(x): x*x, B)
    
    C=[1,4,1]となります。

    文字列を分割したり、リストを連結して文字列にしたり。

    >>> s1 = "1;2;345;"
    >>> A=s1.split(";")
    ['1', '2', '345', '']
    >>> "#".join(A)
    '1#2#345#'
    
    連結する方ですが、セパレータの文字列のメソッドの引数に配列を入れます。
    ちょっと独特。

    fork()

    Pythonでもfork()。osモジュールの中にあります。
    import os,time
    
    child = os.fork()
    if child == 0:
        print "I'm a child"
        time.sleep(3)
    else:
        print "I'm a parent"
        os.wait()
        print "Joined"
    
    対話式のシェルで迂闊に
    >> os.fork()
    
    って打つと、確率1/2でどちらかのプロセスが反応して意味不明に…

    ファイルが存在するかどうか

    ファイルが存在するかどうかの判定は、Perlならファイル存在演算子-eが使えるけど、Pythonでは
    import os
    
    ret = os.path.exists("/tmp/hoge")
    print ret
    
    ファイルが存在すると、retがTrueになる。

    exceptionの受け方。

    下の例で、check()は2,4,6の場合のみ成功し、それ以外の場合は例外を投げます。
    例外が出たらちゃんと受けて(Java的にはcatchして)、次の値を試します。
    def check(candidate):
        if not candidate in [2,4,6]: raise "Exception"
        return "OK"
    
    
    for i in range(0,8)
        ret = ""
        try :
            ret = check(i)
            break
        except :
            continue
    
    print i, ret
    
    あと、知らなかったんだけど、sys.exit()で返り値を付けると、これがexceptionのようにraiseされてくる。
    try:
       sys.exit(1)
    except Exception,e:
       print "Caught %s"%(e)
    
    とすると、'1'を受け取れる。

    リスト内包表現

    for文を[]の中に書く書き方。例えば、
    sum([i**2 for i in A]) ** 0.5
    
    で、ベクトルのノルム(長さ・二乗和のルート)が取れる。

    emacs

    Emacsのpythonモードで複数行をまとるてインデントするには、C-c >とタイプ

    for文中で、対象リストをいじる

    for文で回しているリストの中身を、ループの中でいじると変なことが起こる。
    A = [1,2,3,4,5]
    for a in A:
        print a
        if a == 2:
             A.remove(a)
    
    リストに2が現れたら削除したので、この結果は1,2,3,4,5と期待されるが、 実際には1,2,4,5,になる。(リストがずれたから)
    そこで、元のリストをいじらずに、skipリストを作ってみる。素数を求めるプログラムの例。
    A = range(2,100)
    skips = []
    
    for i, a in enumerate(A):
        if i in skips: continue
        print a
        for j, b in enumerate(A[i:]):
            if b % a == 0:
                skips.append(j)
    

    リスト内包表現: 例えばリストの足し算。(python2以降限定?)

    A = [1,2,3]
    B = [4,5,6]
    C = [A[i]+B[i] for i in range(3)]
    
    C=[5,7,9]になります。
    かなり強力です。
    A = ["Hello", "Goodbye"]
    B = ["kei", "rei"]
    C = [x + ' ' + y for x in A for y in B]
    
    C=["Hello kei", "Hello rei", "Goodbye kei", "Goodbye rei"]ってなります。

    in

    in ってすごく便利。
    s = "hello, world!"
    p = "o, w"
    if p in s 
        print "Matched"
    
    てすると、MAtchedが表示されます。

    ファイルのパーミッションを調べる

    import os
    os.access("/tmp", os.W_OK)
    
    これで、/tmpに書けるかどうか調べられる。

    ランダムな文字列を生成

    import random
    
    def random_str(self, length = 20):
        ret = ""
        for i in range(length):
            ret += random.choice(string.ascii_letters)
        return ret
    

    コマンドライン引数を一覧表示。

    import sys, os
    
    def E(m):
        os.write(2, m)
    
    for i,arg in enumerate(sys.argv):
        E("%d : %s \n", i, arg)
    

    fdopen

    fdopenで、ファイルディスクリプタ番号を決め打ちして開く。
    決まっているファイルディスクリプタは、0:stdin, 1:stdout, 2:stderr。
    fd_in = os.fdopen(0, "r")
    fd_out = os.fdopen(1, "w")
    
    こうすると、fd_inはsys.stdinと、fd_outはsys.stdoutと等価に使える。
    fd_in.readline()
    fd_out.write("hello")
    

    長い文字列連結

    文字列を改行で区切って書くと連結される。
    s = "abc"\
    "def"\
    "ghi"
    
    は、
    s = "abc" +
      "def" +
      "ghi"
    
    と同じ。

    N以下の素数を全て出力。

    Bloom filterの勉強をしてたら、素数が欲しくなりました。
    N = 10000
    A = [True] * N
    
    for i in range(2,N):
        if not A[i] : continue
        print i
        for j in range(i*2,N,i):
            A[j] = False
    

    指定したディレクトリ以下の csv を全てグラフ化

    import os
    
    def print_plot(typ, f):
        csv = os.path.join(typ, f)
        png = os.path.join(typ, f[:-3] + "png")
        print """set terminal png;
    set out "%(png)s";
    set datafile separator ",";
    plot "%(csv)s" index 0 using 0:1 with lines title "X", "%(csv)s" using 0:2 with lines title "Y", "%(csv)s" using 0:3 with lines title "Z";
    #plot "%(csv)s" using 0:1,
    """%locals()
    
    for typ in ["armup", "armdown", "normal"]:
        for f in os.listdir(typ):
            if f.endswith(".csv"):
                print_plot(typ, f)