subprocessモジュールを使って子プロセスにデータを送ったり、結果を受け取ったりする

このまえ、os.system関数で外部コマンドを実行するやり方を書いたけど、どうもsubprocessモジュールを使うやり方のほうが新しいらしい。subprocessモジュールの Popenクラスを使うと、子プロセスにデータを送ったり、結果を受け取ったりできる。

cf. http://docs.python.jp/2.7/library/subprocess.html

まずは、結果を受け取る方法から。

import subprocess

p = subprocess.Popen("ls *.py", stdout=subprocess.PIPE)
files = p.communicate()[0].split("\r\n")
for file in files:
    print file

subprocess.Popenオブジェクトを作るには、第1引数にコマンドを指定する。結果を受け取るにはstdout=subprocess.PIPE を指定しておく。で、communicateメソッドで結果を取得する。

実行結果:

^o^ > python test_subprocess.py
argv.py
bubble_sort.py
count_files.py
counting_sort.py
csv_read.py
csv_write.py
default_arg.py
dict.py
dict_keys.py
dictionary.py
enumerate.py
exception1.py
exception2.py
exception3.py
false.py
fib.py

(以下略)

次に、子プロセスにデータを渡す方法。supbrocess.Popenオブジェクトを作るときに、stdin=subprocess.PIPEを指定しておいて、communicateメソッドの引数としてデータを渡す。

import subprocess

p = subprocess.Popen("python file_input.py", stdin=subprocess.PIPE)
data = open("sample.txt", "r").read()
p.communicate(data)

実行結果:

^o^ > python test_subprocess2.py
<stdin> 1 : Mon
<stdin> 2 : Tue
<stdin> 3 : Wed
<stdin> 4 : Thu
<stdin> 5 : Fri
<stdin> 6 : Sat
<stdin> 7 : Sun

期待通りの出力が得られた。

最後に両方をやってみよう。

import subprocess

p = subprocess.Popen("python file_input.py", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
data = open("sample.txt", "r").read()
output = p.communicate(data)[0]
for line in output.split("\r\n"):
    print line

実行結果:

^o^ > python test_subprocess3.py
<stdin> 1 : Mon
<stdin> 2 : Tue
<stdin> 3 : Wed
<stdin> 4 : Thu
<stdin> 5 : Fri
<stdin> 6 : Sat
<stdin> 7 : Sun

上のと同じ出力だけど、これは子プロセスから結果を受け取ったPythonスクリプトが出力したもの。

Pythonから外部コマンドを実行する

osモジュールのsystem関数を使う。

>>> import os
>>> os.system("ls *.py")
argv.py             function.py            person.py
bubble_sort.py      game_score.py          person2.py
count_files.py      get_feed.py            person2a.py
counting_sort.py    get_feed2.py           person2b.py
csv_read.py         hello.py               primes.py
csv_write.py        httphead.py            primes2.py
default_arg.py      if.py                  print.py
dict.py             inheritance.py         print_to_file.py
dict_keys.py        inheritance2.py        quick_sort.py
dictionary.py       inheritance3.py        range.py
enumerate.py        inheritance3a.py       reduce.py
exception1.py       iterator.py            return_function.py
exception2.py       join.py                set.py
exception3.py       lambda.py              set_add_remove.py
false.py            list_append.py         sort.py
fib.py              list_comprehension.py  split.py
file_input.py       list_extend.py         strip.py
file_read.py        list_index.py          transpose.py
file_readline.py    list_pop.py            true_or_false.py
file_readlines.py   list_remove.py         true_or_false2.py
file_write.py       list_reverse.py        tuple.py
file_writelines.py  ljust.py               upper_lower.py
filecount.py        map.py                 url_get.py
fizzbuzz.py         merge_sort.py          walk.py
fizzbuzz2.py        mkmd5.py               while.py
for.py              mywc.py
0

最後の0は関数の返り値。

datetimeクラス

timeモジュールではエポック秒やtime.struct_timeオブジェクトを扱うので、ちょっと使いにくい。その点、datetimeモジュールのdatetimeクラスは日付や時刻の比較を直接できて使いやすいようだ。

>>> from datetime import datetime
>>> t = datetime.today()
>>> t
datetime.datetime(2013, 2, 17, 22, 38, 52, 593000)
>>> t1 = datetime(2013,1,1)
>>> dt = t - t1
>>> dt
datetime.timedelta(47, 81532, 593000)

datetimeクラス同士の引き算で得られるのは、timedeltaという時間差を表すクラスのオブジェクト。3つの数値はそれぞれ、日、秒、マイクロ秒を表している。今日は今年になってから47日目ってことだな。

timeモジュールにもあったstrftimeメソッドもある。

>>> t.strftime("%Y-%m-%d %H:%M:%S")
'2013-02-17 22:38:52'

ほかにも便利そうなメソッドがたくさんあるけど、詳しくはマニュアルで。

cf. http://docs.python.jp/2.7/library/datetime.html

時間がないので、今日はここまで。

timeモジュール

日付、時刻を処理するtimeモジュールを利用するには、抑えておくべきものが二つある。

  • 1970年1月1日0時0分0秒からの経過秒数を表すエポック秒
  • timeモジュールでの時刻の表現といえる time.struct_timeシーケンス

timeモジュールの関数ではこの二つをよく使う。

gmtime

エポック秒からUTCにおけるtime.struct_timeシーケンスを得る。

>>> time.gmtime(1000000000)
time.struct_time(tm_year=2001, tm_mon=9, tm_mday=9, tm_hour=1, tm_min=46, tm_sec
=40, tm_wday=6, tm_yday=252, tm_isdst=0)

localtime

エポック秒からローカルな時刻のtime.struct_timeシーケンスを得る。

>>> time.localtime(1000000000)
time.struct_time(tm_year=2001, tm_mon=9, tm_mday=9, tm_hour=10, tm_min=46, tm_se
c=40, tm_wday=6, tm_yday=252, tm_isdst=0)

ctime

エポック秒から日付・時刻を表す文字列を得る。

>>> time.ctime(1000000000)
'Sun Sep 09 10:46:40 2001'

ちなみに、gmtime、localtime、ctimeともエポック秒を省略すると現在の時刻が使われる。

asctime

ctimeと違って、time.struct_timeシーケンスから文字列を得る。

>>> time.asctime(time.localtime())
'Sat Feb 16 20:30:02 2013'

time

UTCにおける現在のエポック秒を得る。返り値は浮動小数点数。

>>> time.time()
1361015278.904

mktime

localtimeの逆を行う関数。ローカル時刻のtime.struct_timeシーケンスからエポック秒を得る。

>>> t = time.localtime()
>>> time.mktime(t)
1361014493.0

strftime

time.struct_timeシーケンスから、指定したフォーマットの文字列を得る。

>>> t = time.localtime()
>>> time.strftime("%Y-%m-%d %H:%M:%S", t)
'2013-02-16 20:37:57'

strptime

strftimeの逆。フォーマットされた日付・時刻からtime.struct_timeシーケンスを得る。

>>> s = "2013-02-16 20:40:00"
>>> t = time.strptime(s, "%Y-%m-%d %H:%M:%S")
>>> t
time.struct_time(tm_year=2013, tm_mon=2, tm_mday=16, tm_hour=20, tm_min=40, tm_s
ec=0, tm_wday=5, tm_yday=47, tm_isdst=-1)

主な関数はこんなところ。マニュアルにはほかにもいくつもの関数が書かれている。

cf. http://docs.python.jp/2.7/library/time.html

マージソート

import random

def merge_sort(lis):
    l = len(lis)
    if l <= 1:
        return lis
    else:
        h = l/2
        lis1 = lis[0:h]
        lis2 = lis[h:l]
        return merge(merge_sort(lis1), merge_sort(lis2))

def merge(lis1, lis2):
    merged = []
    while True:
        if len(lis1) == 0:
            merged += lis2
            break
        elif len(lis2) == 0:
            merged += lis1
            break
        else:
            if lis1[0] <= lis2[0]:
                merged.append(lis1.pop(0))
            else:
                merged.append(lis2.pop(0))
    return merged


l = range(1,10) * 2
random.shuffle(l)
print "unsorted:", l
l2 = merge_sort(l)
print "sorted: ", l2    

実行例:

^o^ > python merge_sort.py
unsorted: [6, 9, 6, 3, 4, 1, 4, 5, 3, 1, 2, 8, 7, 2, 5, 7, 9, 8]
sorted:   [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

バブルソート

バブルソート(bubble sort)もやってみた。

import random

def bubble_sort(lis):
    l = range(1, len(lis))
    l.reverse()
    for i in l:
        for j in range(1, i+1):
            if lis[j-1] > lis[j]:
                lis[j], lis[j-1] = lis[j-1], lis[j]
    return lis


l = range(1,10) * 2
random.shuffle(l)
print "unsorted:", l

l2 = bubble_sort(l)
print "sorted: ", l2

実行結果:

^o^ > python bubble_sort.py
unsorted: [7, 6, 8, 2, 1, 4, 2, 6, 8, 3, 4, 5, 9, 9, 3, 5, 7, 1]
sorted:   [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

クイックソート

分布数え上げソートをやったついでに、クイックソート(quick sort)もやってみた。

import random

def quick_sort(lis):
    if len(lis) == 0:
        return []
    p = lis.pop(0)
    left = [x for x in lis if cmp(x, p) < 0]
    right = [x for x in lis if cmp(x, p) >= 0]
    return quick_sort(left) + [p] + quick_sort(right)


l = range(1,10) * 2
random.shuffle(l)
print "unsorted:", l

l2 = quick_sort(l)
print "sorted: ", l2

実行例:

^o^ > python quick_sort.py
unsorted: [2, 7, 9, 1, 8, 6, 3, 5, 1, 5, 2, 4, 8, 7, 3, 9, 4, 6]
sorted:   [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

分布数え上げソート

PHPでやってる人を見かけたので、Pythonでやってみた。

cf. 第4回 分布数え上げソート – noldor’s diary

分布数え上げソート(counting sort)ってのは、要素の最小値と最大値がわかっている場合に

  1. 各要素の出現回数を数える
  2. 小さい順に要素を出現回数分だけ出力する

っていうアルゴリズムだ。基本的に2回ループをまわすだけなので速い。

import random

def counting_sort(lis, min, max):
    counter = [0] * (max + 1)
    for i in lis:
        counter[i] += 1
        r = []
        for i in range(min, max+1):
            for j in range(counter[i]):
                r.append(i)
                return r


l = range(1,10) * 2
random.shuffle(l)
print "unsorted:", l

l2 = counting_sort(l, 1, 9)
print "sorted: ", l2

実行例:

^o^ > python counting_sort.py
unsorted: [1, 3, 7, 6, 9, 4, 7, 5, 2, 6, 9, 5, 1, 4, 8, 8, 3, 2]
sorted:   [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

xml.etree.ElementTreeを使ってRSSから記事のタイトルを取り出す

先週のエントリで、RSSフィードから記事のタイトルを表示するスクリプトを書いた。ただ、このときにはタイトルを取り出すのに正規表現を使ったから、記事のタイトルだけじゃなくてブログのタイトルも取り出されてしまっていた。
そこで、今回は xml.etree.ElementTree を使って記事のタイトルだけを取り出してみた。といっても使い方はよくわかってないんだけど。

cf. http://docs.python.jp/2/library/xml.etree.elementtree.html

import sys
import urllib
import xml.etree.ElementTree

url = sys.argv[1]

src = urllib.urlopen(url)
doc = xml.etree.ElementTree.parse(src)

for title in doc.findall(".//item/title"):
    print title.text

xml.etree.ElementTree.parse はファイル名またはファイルオブジェクトを受け取ってDOMを返してくれる。findallはXPath(?)を受け取ってエレメントを返してくれる。・・・らしい。よくわからないけどこれで何とかなった。

実行例:

^o^ > python get_feed2.py https://blog.panicblanket.com/feed
リストのスタック系メソッド
os.walk関数を使ってファイル数を列挙する
urllibモジュールの超簡単なサンプル(その3)
urllibモジュールの超簡単なサンプル(その2)
urllibモジュールの超簡単なサンプル
変数に関数と同じ名前をつけてはいけない
== 演算子と is 演算子
randomモジュール
fileinputモジュール
Rubyで点数を集計するとき、あなたはどうしてますか? をPythonで

リストのスタック系メソッド

Python でリストをスタックとして使おうとしたときにはちょっとやりにくい。popはあるけどpushがないし、shiftもunshiftもない。なのでちょっとメモ。Ruby のスタック系メソッドとの比較で書いておく。

Ruby Python
pop pop()
push(item) append(item)
shift pop(0)
unshift(item) insert(0, item)

実行例:

>>> l = ['a', 'b', 'c', 'd', 'e']
>>> l.pop()
'e'
>>> l
['a', 'b', 'c', 'd']
>>> l.append('E')
>>> l
['a', 'b', 'c', 'd', 'E']
>>> l.pop(0)
'a'
>>> l
['b', 'c', 'd', 'E']
>>> l.insert(0, 'A')
>>> l
['A', 'b', 'c', 'd', 'E']