Python: 02. 標準ライブラリ - ikymrkw/pydepot GitHub Wiki

実行環境 (sys)

sys.stdin  # 標準入力ストリーム
sys.stdout  # 標準出力ストリーム
sys.stderr  # 標準エラー出力ストリーム

コマンドライン引数

 import sys
 argvs = sys.argv  # 引数リスト
 # 引数リストには python コマンドの引数が格納される。
 # 0にスクリプト名、1、2、……に引数。
 #
 # 細かく言うと:
 # - python abc.py x y z と実行した場合、
 #   argvs[0] == "abc.py", argvs[1] == "x", ... となる。
 # - Unix-like OS で shell bang を使い、./abc.py x y z と実行した場合、
 #   argvs[0] == "./abc.py", argvs[1] == "x", ... となる。

argparseモジュールを使うともっといろいろできる。

 import argparse
 
 parser = argparse.ArgumentParser()
 parser.add_argument("infile", type=file)
 parser.add_argument("--foo")
 args = parser.parse_args()
 lines = read_lines(args.infile)

終了

 sys.exit(exitcode)

プログラムの終了時に関数を呼びたいときは、atexitモジュールを使う(かつては sys.exitfunc だったが、2.4以降では deprecated)。Ctrl-Cで終了された場合も呼ばれる。

 import time
 import atexit
 
 def finisher():
     print "Finished!"
 
 atexit.register(finisher)
 
 while True:
     print "waiting..."
     time.sleep(1)

サブプロセス

コマンドの実行結果(標準出力、エラー出力)を受け取る。

 import subprocess
 p = subprocess.Popen(['cmd', 'arg1', 'arg2'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 (s_stdout, s_stderr) = p.communicate()
 if p.returncode==0:
     print s_stdout

p.communicate() はサブプロセスが終了するまで待機する。

p.communicate() 時の標準入力・出力・エラー出力はデフォルトでは Python のプロセスのものが使われる。 したがって、出力は python を実行したシェルに出力されたりする。出力したくない場合は、上記のように PIPE に接続する。入力も同様で、PIPE につなげば communicate(input) の引数が入力に使われる(文字列型?)

ファイル操作

ビルトイン

 f = open('foo.txt', 'r')
 data = f.read()

なお、モードを 'rU' とすることで改行文字の違いを無視できる。'rb' ならバイト読み出し。

1行ずつ読み込み

 for line in f: ...
 for line in f.readlines(): ...  # in f と同じ?
 for line in sys.stdin: ...  # 標準入力の場合

ioモジュール

 import io
 f = io.StringIO(text)
 lines = f.readlines()

 # ただしこれと同じことが以下でできる:
 lines = text.splitlines()

osモジュール

https://docs.python.org/ja/3/library/os.html

 chdir('path')
 getcwd()
 remove('path')
 rename('before', 'after')
 mkdir('path'[, 'mode']) 
 makedirs('path'[, 'mode'][, exist_ok=True])  # 中間ディレクトリも作る。exist_ok=Trueなら既存でもOK
 rmdir('path')
 removedirs('path')
 listdir('path')
 walk('path'[, topdown=True][, onerror=None])  # ジェネレータを返す。
     # 要素は (ディレクトリ名, 子ディレクトリのリスト, ファイルのリスト) の三つ組(タプル)。
 os.path.exists('path')
 os.path.isfile('path')
 os.path.isdir('path')

文字・文字列・コンソール出力

オブジェクトの文字列化一般

s = str(obj)

オブジェクトの __str__() メソッドを定義することで str() が返す文字列を制御することができる。

str() は人間が(デバッグ用に)読む文字列を返すことが期待されている。

一方、repr() という関数もあり、__repr__() メソッドに対応している。こちらは eval() で元に戻せる文字列が期待されている。

フォーマッティング

print

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Pretty Print (pprint)

import pprint
pprint.pprint(obj)
s = pprint.pformat(obj)

フォーマット済み文字列リテラル

s = f"Results of the {year} {event}"

中括弧内には式を書ける。

str.format() メソッド

s = "Results of the {} {}".format(year, event)

中括弧内に番号を書くことで、引数の位置を指定できる。

s = "{1} and {0}".format("egg", "chicken")

中括弧内にキーワード引数名で指定することもできる。

s = "This {food} is {adjective}".format(food='soup', adjective='hot')

中括弧内には書式指定を書くこともできる。

s = "{1:2d} {0:3d} {2:.4f}".format(id, count, probability)

文字符号化

文字列からの変換でよければ str.encode() メソッドでできる。

 'ABC'.encode('utf-8')   # = b'ABC'  # デフォルトは 'utf-8'
 'ABC'.encode('utf-16')   # = b'\xff\xfeA\x00B\x00C\x00'
 # 文字符号化以外に hex も使える
 'ABC'.encode('hex')     # = '414243'
 '414243'.decode('hex')  # = 'ABC'
 
 # str.decode() はない。
 # codecs.encode(s, encoding) や .decode(s, encoding) のほうが汎用性は高い

ストリームを使いたい場合は codecs パッケージを使う。

 codecs.getreader("UTF-8")(sys.stdin)   # ストリームからUTF-8で読み込む
 codecs.open("filename", "r", "UTF-8")  # ファイルからUTF-8で読み込むためのファイルストリームを生成

なお、Python 3 では文字列(文字ストリーム)とバイト列(バイトストリーム)は厳密に区別されるようになったため、文字ストリームを返す sys.stdin をデコードすることはできない。sys.stdin.detach() とするとバイトストリームを得られるので、こちらを使うと良い。

Base64 と uuencode

 'abc'.encode('base64')
 'abc'.encode('uu')

正規表現

re パッケージを使う。[https://docs.python.org/3/library/re.html#module-re]

 import re
 
 m = re.match("re", input)
 if m:
     print m.group(1)
 m = re.search("re", input)
 pattern = re.compile("re")
 re.split(',', 'abc,def,ghe')  # = ('abc', 'def', 'ghi')

copy

 copy.copy(obj)      shallow copy
 copy.deepcopy(obj)  deep copy

双方向キュー (deque)

http://docs.python.jp/2.5/lib/deque-objects.html

 from collections import deque
 q = deque()   # or deque(iterable)
 first = q[0]  # リストと同様のアクセスも可能
 tail = q.pop()
 head = q.popleft()
 q.append(x)
 q.appendleft(y)
 q.extend(iterable)
 q.extendleft(iterable)

ただし、q[1:2]のようなスライスはできない模様。

時間

time --- 簡易な方法

POSIXタイムスタンプ(epochからの秒数、ただしタイムゾーンによって異なる)を得るには

import time
 
time.time()
=> 1445241301.727  # ローカル時間のPOSIXタイムスタンプ(システム依存の浮動小数点)
time.ctime()
=> 'Mon Oct 19 16:58:38'   # ローカル時間の文字列
                           # time.asctime(time.localtime()) と同じ
                           # ctime() や localtime() はタイムスタンプを与えることもできる

time パッケージはローカル時間(とローカルタイムゾーン)しか扱えないので、 UTC時間が欲しい場合は自分で計算する必要がある。もしくは、下記の datetime を使う。

datetime の取得

from datetime import datetime

t1 = datetime.now()    # ローカル時間での現在時刻の datetime オブジェクト(tz情報なし)
t2 = datetime.now(tz)  # 指定タイムゾーンでの現在時刻の datetime オブジェクト(指定したtz情報あり)
t3 = datetime.utcnow() # UTCでの現在時刻の datetime オブジェクト(tz情報なし)

t1.timestamp()  # datetimeオブジェクトからPOSIXタイムスタンプへ
 
# ローカル時刻のタイムスタンプから datetime オブジェクトを得る
datetime.fromtimestamp(timestamp)      # (1) ローカル時刻 timestamp がローカルTZでは何時か
datetime.fromtimestamp(timestamp, tz)  # (2) ローカル時刻 timestamp が tz では何時かを示す
datetime.utcfromtimestamp(timestamp)   # (3) ローカル時刻 timestamp が UTCでは何時かを示す
# 注: (1)と(3)はタイムゾーン情報のない datetime オブジェクトを返すことに注意

# UTC時刻のタイムスタンプから UTC での datetime オブジェクトを得る
from datetime import timezone
datetime.fromtimestamp(utc_timestamp).replace(tzinfo=timezone.utc)

# さらに
# .astimezone() をすればローカルTZでの時刻に、
# .astimezone(tz) とすれば目的のTZでの時刻に、
# それぞれ変換できる。

タイムゾーンを得る方法:

from datetime import datetime, timezone, timedelta

# A. UTCのタイムゾーン
tz_utc = timezone.utc 
# B. ローカルのタイムゾーン(システムに依存する)
tz_local = datetime.now().astimezone().tzinfo  # astimezone() はローカル時刻とみなしてTZ情報を付ける
# C. 数値指定のタイムゾーン
tz_there = timezone(timedelta(hours=-9))
# D. 名前指定のタイムゾーン
# Python標準では名前は未対応。外部パッケージ pytz を使うのが一般的

文字列化:

from datetime import datetime, timezone

# こだわりがなければ ISO format がわかりやすい。タイムゾーンには注意。
dt1 = datetime.now()
print(dt1.isoformat())  # => '2024-05-30T11:22:33.234567'  # 暗黙的にローカルTZ
dt2 = dt1.astimezone()  # 引数なしはローカルTZ情報を付ける
print(dt2.isoformat())  # => '2024-05-30T11:22:33.234567+09:00'  # 明示的にローカルTZ

その他の操作

差分を取るには、time.time()を引き算してもよいが、datetimeなら差分オブジェクトになる。

 from datetime import datetime
 x = datetime.utcnow()
 ...
 y = datetime.utcnow()  # 差だけを見るなら x, y ともに now() でも構わない
 d = y - x   # d は timedelta オブジェクト(日, 秒, マイクロ秒のみ保存されている)
 d.days            # 日部分
 d.seconds         # 秒部分
 d.microseconds    # マイクロ秒部分
 d.total_seconds() # 日・秒・マイクロ秒を秒換算した浮動小数点数を返す

sleep するには

 import time
 time.sleep(1.5) # 単位は秒

マルチスレッド (threading)

 import threading
 
 def main():
     worker = threading.Thread(target = f, args = ("abc", ))
     worker.start()
 
 def f(msg):
     while True:
         print msg
         time.sleep(1)

Thread生成時に daemon を指定できるが、いきなり止まるので推奨されない。 Eventなどを使って適切に処理すべき。

 ev = threading.Event()
 ev.is_set()
 ev.set()
 ev.clear()

乱数

random モジュールに含まれる。

 randint(a, b)       # a <= x <= b のランダムな整数 x を返す
 uniform(a, b)       # randint と同様だが、実数を返す
 random()            # 0以上1以下の実数を返す
 randrange(start, end, step)  # range と同様だが、ランダムに並べ替えたシーケンスを返す
 choice(シーケンス)  # シーケンスからランダムに選んだ一つを返す
 shuffle(シーケンス, 関数)  # シーケンス自身をシャッフルする。関数は省略可だが、random()同様の乱数関数を指定する。
 sample(シーケンス, n) # シーケンスからn個選んで返す。同じ要素は重複して選ばない。
 seed(x)             # シードする。xは省略可。randomモジュールが読まれた時点で呼ばれている。

ただし、これらは暗号学的安全ではない。必要なら PyCrypto モジュールの Crypto.Random を使う(後述)。

各種データの入出力

シリアライゼーション (Pickle)

Pickle はシリアライゼーションのためのモジュール。

 obj = range(100)
 from cPickle import dump
 output = open('obj.pkl', 'wb')
 dump(obj, output, -1)
 output.close()

 from cPickle import load
 input = open('obj.pkl', 'rb')
 obj = load(input)
 input.close()

CSV

読み込み

 import csv
 f = open("abc.csv", "rb")
 for row in csv.reader(f):
     print row  # row は文字列のリスト

XML

xml.etree.ElementTree を使う。[http://docs.python.jp/2/library/xml.etree.elementtree.html]

import xml.etree.ElementTree as ET
tree = ET.parse('filepath')
root = tree.getroot()

root.find('.//customer')  # XPathサブセットで要素を指定

名前空間については、名前空間のマップを指定して検索することができるらしいが、 自分のマシンに入っていたETでは未対応だった。この場合、"{namespaceuri}tagname" というように 中括弧で囲って名前空間URIを書くしかないらしい。

JSON

 import json
 
 text = json.dumps(data)
 text = json.dumps(data, indent=4, sort_keys=True)
 # data は、基本は Python のリスト/タプルまたはディクショナリだが、
 # 文字列を渡すと JSON エンコードしてくれる。
 # dumps のフォーマッティングには jinja2 が必要かもしれない。
 
 data = json.loads(jsontext)

バイトデータ

メッセージダイジェスト (hashlib)

 import hashlib
 hashlib.md5('abc').hexdigest()  # 16進文字列を返す。他に sha1(), sha256() など。

バイト列 (bytes, bytearray)

Python 2 では文字列型がバイトの列なので、バイト列を文字列型として扱うこともできるが、その要素は文字(要素数1の文字列)なので整数としての扱いが少々不便(chrやordを入れないといけない)。

bytes と bytearray は 0~255 の int の配列として使える。ほぼ文字列型と同様のメソッドが利用可能。 両者の違いは bytes が不変 (str や tuple と同様)、bytearray が可変 (list と同様) なことで、 それ以外はほぼ同様の操作が可能。

 # 生成
 bytearray()
 bytearray([1, 2, 3, 255])
 bytearray.fromhex('abcd')
 
 # 参照
 bb[1]
 bb[1:3]
 
 # 結合
 bb1 + bb2
 bb1 * 4
 
 # 文字列化
 ''.join(["%02x" % b for b in bb])   # 16進数表現。hex()は使えない。
 str(bb)                             # バイト文字列

Enum

from enum import Enum

class Land(Enum):
    HOKKAIDO = 0
    HONSHU = 1
    SHIKOKU = 2
    KYUSHU = 3    # 値は整数じゃなくてもよい。文字列などでもなんでもよい。

start = Land.HOKKAIDO
from enum import Enum, auto

class X(Enum):
    A = auto()
    B = auto()
from enum import Enum
Language = Enum('Language', 'EN JP FR')
lang = Language.EN