CmhaDSO 組込み標準機能


はじめに

Pythonコード

Pythonコードでは構造化をインデント(字下げ)で表現する。 コロンはインデントされたブロックの開始を示し、ブロック内の各行はインデントを揃える必要がある。 インデントにはタブもしくはスペースを使用できる。 #から行末まではコメント(注釈)として無視される。 コードスタイルとしては PEP 8 が一般的に推奨され、以下にその一部を摘要する。

# Pythonコード例: Fibonacci sequence
a, b = 0, 1
while b < 100:
    print(b, end = ",")
    a, b = b, a + b

# 出力: 1,1,2,3,4,5,13,21,34,55,89

オブジェクトモデル

Pythonは一貫したオブジェクトモデルを採用し、オブジェクトには対応する型がある。 オブジェクトには通常、属性とメソッドが定義されている。 属性はそのオブジェクトの中に格納されている別のオブジェクトであり、メソッドはオブジェクト内部構造にアクセスするための手段を提供する関数である。 ピリオドを使って、属性やメソッドにアクセスすることができる。

s = 'hoge'
s.<Press Tab>     # 文字列の属性とメソッドの一覧が表示される。

NoneはPythonにおけるNull(値がない)である。 NoneType型オブジェクトの唯一のインスタンスである。 関数の引数の既定値などに使用される。

変数

変数は型宣言せずに使用することができる。

a = 1
a + 1   # 出力: 2
変数を代入すると等式の右辺にあるオブジェクトへの参照が作られる。 型情報はオブジェクト自体に格納されており、オブジェクトの参照には型がない。
# 以下の例においてa, bが同一のオブジェクトを参照していることに留意すること。
a = [0, 1]
b = a
a.append(2)
b             # 出力: [0, 1, 2]。

# 以下の例にも注意せよ。
a = 3
b = [1, 2, a]   # b: [1, 2, 3]。
a = 4
b               # b: [1, 2, 3]。

# 以下の例においてaには別の型の値を代入することができることに留意すること。
type(a)              # 出力: <class 'list'>。
a = 1
type(a)              # 出力: <class 'int'>。

有効な名前はアルファベット、数字、アンダースコアを含むが、数字から始めることはできない。 また以下により出力される予約語も使用できない。

__import__('keyword').kwlist

論理と比較

真値はTrueまたは1、偽値はFalseまたは0で表す。 以下の値はFalseとされる。

論理演算子にはand, or, notがある。 andとorは短絡評価(演算対象の評価が左から右に実施され、結論が出た時点で評価が停止)される。 演算子の優先順位はor > and > notの順であり、たとえば「A and not B or C」は「(A and (not B)) or C」と解釈される。

x and y xとyの両者が真の場合のみTrueを返す。
x or y xまたはyのどちらか、もしくは両者が真の場合にTrueを返す。
not x xが偽ならTrue、真ならFalseを返す。

比較演算は論理演算よりも優先順位が高いため、たとえば「not a == b」は「not (a == b)」と解釈される。 比較は連鎖することができ、x < y and y < zはx < y < zのように表現できる。

< より小さい
<= 以下
> より大きい
>= 以上
== 等しい
!= 等しくない
is 同一のオブジェクトである
is not 同一のオブジェクトでない

制御文

if文は条件式を評価する。 もし式がTrueと評価された場合、コードブロックが実行される。 elif節を追加すれば別条件式の評価が追加される。 else節を追加すれば全ての条件式がFalseである場合の実行コードを指定できる。

if expr1:
    codeBlock1  # expr1がTrueなら実行。
elif expr2:
    codeBlock2  # expr1がFalse、expr2がTrueなら実行。
elif expr3:
    codeBlock3  # expr1,2がFalse, expr3がTrueなら実行。
else:
    codeBlock4  # expr1,2,3がFalseなら実行。

if-else文は三項演算子を用いて簡略表現できる。

code_in_case_of_True if expr else code_in_case_of_False

for文ではiterableなオブジェクト(反復可能体)の値を変数に保持しながらコードブロックを反復実行する。 基本形式は以下の通りである。

for var in iterable:
    codeBlock
range関数は等差級数を生成するが、シーケンス要素を連続的に返すことでリストを作成しないためメモリを節約する。 iterableなオブジェクトを期待する関数や構造を反復子(イテレータ)といい、for文やlist関数などがこれに該当する。
for i in range(10):
    print(i, end=",")

# range(10)の場合の出力: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9。range(0, 10)と等価。
# range(2, 5)の場合の出力: 2, 3, 4。
# range(0, 10, 2)の場合の出力: 0, 2, 4, 6, 8。増分指定をした場合の例。

while文では条件式がTrueである限りコードブロックを反復実行する。 基本形式は以下の通りである。

while expr:
    codeBlock

forまたはwhileのコードブロック内でcontinue文を用いると、それ以降のコードを飛ばして次の反復に進む。 コードブロック内でbreak文を用いると、(最も内側の)forまたはwhile文から抜ける。 for文やwhile文にはelse節を加えることができる。 構文的には文を必要とするが何のプログラムも実行しない場合にはpass文を用いる。

主要な型

数値と論理値

数値型には実数である整数(int型)や浮動小数点数(float型)と複素数(complex型)がある。 小数は3.14や1.0e-10のように表現することができる。 数値型は以下の演算に対応している。
float()は文字列"nan"と"inf"を接頭辞"+"または"-"と共に、非数(Not a Number(Nan))や正、負の無限大として受け付ける。

x + y xとyの和
x - y xとyの差
x * y xとyの積
x / y xとyの商
x // y xとyの商を切り下げたもの
x % y x / yの剰余
x ** y xのy乗
pow(x, y) xのy乗
abs(x) xの絶対値の大きさ

実数は以下の演算にも対応している。

math.trunc(x) xを整数に切り捨てる
round(x[, n]) xをn桁に偶数丸めする。nが省略された場合は0が既定値となる。
math.floor(x) x以下の最大の整数を返す。
math.ceil(x) x以下の最小の整数を返す。

その他、 mathcmath モジュールには様々な数学関数がある。

bool型は真偽値を表現し、2つの定数インスタンスTrueとFalseをもつ。 これらの値は比較演算などの条件判断の結果得られる。 and, orおよびorを用いて論理演算を行うことができる。

テキストとバイナリ

str|bytes

テキストデータはstr型オブジェクト、すなわち文字列として処理される。 文字列リテラルは単一または二重引用符で囲む('...', "...")ことで作られる。 複数行に渡る場合は三重引用符で囲む('''...'''又は"""...""")。 str関数を用いれば他のオブジェクトをstr型に変換できる。

文字列はシーケンス型の一種であり、後述する他のシーケンス型と同様にインデックス参照演算子[]を用いて文字列の部分集合を参照することができる。 これをインデックス操作といい、[start:stop]あるいは[start:stop:step]の形式で参照位置を指定する場合はスライシング操作という。 インデックスは0から起算され、[start:stop]の指定では[start, stop)の半開区間が参照される。 startstopの値を省略した場合はそれぞれシーケンスの先頭と末尾を指定したことになる。 負のインデックス値を指定した場合は末尾から起算する。 stepの-1を指定した場合は逆順になる。 スライシングは常に別のシーケンスを返すため、a[:]形式は値のコピーのために使用できる。

s = 'cmhadso'  # cmhadso
               # 0123456  from right
               # 7654321  from left

s[4]           # 出力: 'd'
s[0:4]         # 出力: 'cmha' 
s[:4]          # 出力: 'cmha'
s[4:]          # 出力: 'dso'
s[-4:-2]       # 出力: 'ad'
s[::2]         # 出力: 'chdo'
s[::-1]        # 出力: 'osdahmc'

文字列は+演算子で連結でき、*演算子で反復することができる。

'ba' + 'na' * 2                   # 出力: 'banana'

for i in range(3):
    print("s" + str(i), end=",")  # 出力: s0,s1,s2

その他の文字列操作関数を以下に示す。

'atgc'.upper()              # 出力: 'ATGC'。
'ATGC'.lower()              # 出力: 'atgc'。
'AtgC'.replace('tg', 'TG')  # 出力: 'ATGC'。
'banana'.count('a')         # 出力: 3。
'nnnnatgnnn'.index('atg')   # 出力: 4。出現位置を返す。なければValueErrorを発生する。
'a-b-c'.split('-')          # 出力: ['a', 'b', 'c']。指定されたセパレータで単語を区切りリストを返す。既定のセパレータはスペース。
'-'.join(['a', 'b', 'c'])   # 出力: 'a-b-c'。splitの逆。
'  a b  \t\n'.strip()       # 出力: 'a b'。両端から空白文字(スペース、タブ、改行)を削除する。左端のみの場合はlstrip()、右端のみの場合はrstrip()を用いる。

バックスラッシュ(\)を前置することでエスケープができる。 例えば改行文字は\n、水平タブは\tと表現する。 バックスラッシュ自身を文字列に含める場合は\\のように指定するか、引用符の直前にr接頭辞を付与することでエスケープ処理を無効にする。

s = 'a\ta'
print(s)    # 出力: a	a。

s = 'a\\ta'
print(s)    # 出力: a\ta。

s = r'a\ta'
print(s)    # 出力: a\ta。

reモジュールのcompilesearchfinditersubメソッドを利用して正規表現による検索や置換を実行する。

import re
sbj = 'banana'
ptn = re.compile('na')   # 高速処理のためパターンをcompileする

# --- searchメソッド: 初めにマッチしたものについてのマッチオブジェクトを返す ---
out = ptn.search(sbj)
out.group()              # 出力: 'na'。マッチ文字列を出力する。
out.span()               # 出力: (2, 4)
out.start()              # 出力: 2
out.end()                # 出力: 4

# --- finditerメソッド: 各マッチのマッチオブジェクトについてのイテレータを返す ---
out = ptn.finditer(sbj)
for i in out:
    out.group()
    out.span()

# 出力: 'na'
#       (2, 4)
#       'na'
#       (4, 6)

# --- subメソッド: 置換する ---
# sub(replace, subject [,count=0])
# countは置換回数、値0は全てのマッチを置換するの意味。
ptn.sub('', s)   # 出力: 'ba'

文字列オブジェクトのformatメソッドを用いて以下のように文字列を形式指定して表現することができる。

t = 'interger part of {0:.2f} is {1:d}'
t.format(3.1415, 3)         # 出力: 'integer part of 3.14 is 3'

d = {'a':1, 'b': 2}
'{a}_{b}'.format(**d)       # 出力: '2_1'

encodeメソッドを用いてUnicode文字列をUTF-8バイト列に変換する。 逆にdecodeメソッドを用いてUTF-8バイト列をUnicode文字列に変換する。

s  = 'ほげ'
s8 =  s.encode('utf-8')   # s8: b'\xe3\x81\xbb\xe3\x81\x92'
type(s8)                  # <class 'bytes'>
s8.decode('utf-8')        # 出力: 'ほげ'

リストとタプル

listは最も汎用的なシーケンス型であり、可変長でmutableである。 リストは[]を用いて定義することができ、ネストも許容する。 list関数を用いればlistに型変換することができる。 シーケンス型データに共通に適用可能な操作(インデックス、スライシング、inを用いた要素の所属検査、+を用いた連結、len, max, min)に対応している。

l = [0, [1, 2]]    # ネストされたリストの例
l[1][0]            # 出力: 1

list('str')        # 出力: ['s', 't', 'r']
list(range(3))     # 出力: [0, 1, 2]

tuple は固定長で変更不可能(immutable)なオブジェクトであり、安全なコード作成に利用できる。 リストと比べてメモリ使用量が少なく、タプルに作用する操作の方が速い。 タプルとリストはそれぞれ用途が異なる。 タプルは異種要素からなり、各要素にはアンパッキングやインデックスでアクセスすることが多い。 一方、リストは同種要素からなり、各要素には反復をかけることでアクセスすることが多い。

タプルを作成する。タプルに変換する。

0, 1, 2             # 単なるカンマ区切り。出力: (0, 1, 2)。
()                  # 要素数0のタプル。
0,                  # 要素数1のタプル。
tuple([0, 1])       # 出力: (0, 1)。

タプル(やリスト)を分解してその要素値を変数に代入する。

a, b, c = 0, 1, 2              # a = 0, b = 1, c = 2
a, (b, c) = 0, (1, 2)          # a = 0, b = 1, c = 2
a, b = b, a                    # 変数名を交換する。 a = 1, b = 0。
a, b, *c = 1, 2, 3, 4, 5       # a = 1, b = 2, c = [3, 4, 5]。末端の値が不要な場合、慣例として"*_"という名で値を受け取る。
for a, b in [(1, 2), (3, 4)]:  # 出力:3 7
    print(a + b)

リストの要素を追加(append, insert, extend)または削除(pop, remove, clear, del文)する。

l = ['a', 'c', 'a']

l.append('a')       # 末尾に要素を追加する。l: ['a', 'c', 'a', 'a']。
l.insert(1, 'b')    # 指定位置に要素を挿入する。l:['a', 'b', 'c', 'a', 'a']。
l.pop(2)            # 指定位置の要素を削除・出力する。出力:'c'。l:['a', 'b', 'a', 'a']。
l.pop()             # 引数を指定しない場合は最後の要素を削除・出力する。出力: 'a'。:['a', 'b', 'a']。
l.remove(a)         # 指定要素(先頭から探索し最初に一致したもの)を削除する。l:['b', 'a']。
l.extend(l)         # リストを結合する。"+"よりも高速である。l: ['b', 'a', 'b', 'a']。
l.clear()           # リストの全要素を削除する。 l: []。
del l

リストを複製する。

# b = aでは参照をコピーすることになる。
a = [0, 1]
b = a
a.append(2)
b             # 出力 : [0, 1, 2]。

# 値をコピーするにはa[:]を用いる。
a = [0, 1]
b = a[:]
a.append(2)
b             # 出力 : [0, 1]。

stackは後入先出(Last-In-First-Out, LIFO)のデータ構造であり、append及びpopメソッドを用いればリストをstackとして利用することができる。 queueは先入先出(First-In-First-Out, FIFO)のデータ構造である。 append及びpopメソッドを用いればリストをqueueとして利用することができるが、popはリストの先頭で実行する用途には不向きである。 queueの実装にはcollectionsモジュールのdequeを利用することが望ましい。

from collections import deque
q = deque([0, 1, 2])
q.append(3)     # q: deque([0, 1, 2, 3])
q.popleft()     # 出力: 0。q: deque([1, 2, 3])

指定要素のインデックスや個数を出力する。

l = [1, 1, 1, 2, 1, 2]
l.index(2)         # 値が2である最初の要素のインデックスを返す。出力: 3。
l.count(1)         # 値が1である要素の個数を返す。出力: 4。

sortメソッドやsort関数を用いてリスト要素をソートする。両者にはリスト内容を直接変更するか否かの相違がある。

l = ['fukuoka', 'saga', 'nagasaki']
l.sort()                            # 辞書順。l: ['fukuoka', 'nagasaki', 'saga']
l.sort(key = len)                   # 文字列長昇順。l: ['saga', 'fukuoka', 'nagasaki']    
l.sort(key = len, reverse = True)   # 文字列長降順。l: ['nagasaki', 'fukuoka', 'saga']
sorted(l)                           # 出力: ['fukuoka', 'nagasaki', 'saga']

リストに指定要素が含まれるかをinキーワードを用いて確認する。
ディクショナリやセットはハッシュ値に基づき定数時間で検索できるためリストよりも迅速に検索できる。

l = ['a', 'c']
'b' in l        # 出力: False
'b' not in l    # 出力: True

bisectモジュールを用いてリストの二分探索をする。リストは事前にソートされている必要がある。bisectモジュールのbisect関数は新規要素の挿入位置を返し、insort関数はその位置に要素挿入を行う。

import bisect
l = ['a', 'b', 'b', 'd']
bisect.bisect(l, 'c')  # 出力: 3
bisect.insort(l, 'c')  # l: ['a', 'b', 'b', 'c', 'd']

map関数を用いてリストに関数を適用する。

list(map(lambda x:x*2, [1, 2, 3]))    # 出力: [2, 4, 6]。

[expression for value in collection if condition]という形式で表現を簡略化することができ、これをリスト内包記法という。 フィルター条件式を省略して変換式のみ記述することもできる。

l = ['cat', 'lion', 'tiger']
[i.upper for i in l if len(i) > 3]     # 出力: ['LION', 'TIGER']。

ネストしたリスト内包ではforの部分はネストしている順番に従って、外側から順番に記載されている。

l = [['cat', 'tiger'], ['lion', 'cheetah']]
[i for j in l for i in j if len(i) > 4]    # 出力: ['tiger', 'cheetah']

l = [(1, 2), (3, 4), (5, 6)]
[i for j in l for i in j]      # 出力: [1, 2, 3, 4 ,5 ,6]
[[i for i in j] for j in l]    # 出力: [[1, 2], [3, 4], [5, 6]]

ネストしたリスト内包の別例として行列転置を下に示す。

m = [
    [1, 2, 3],
    [5, 6, 7],
]

[[r[i] for r in m] for i in range(3)]   # 出力: [[1, 5], [2, 6], [3, 7]]
list(zip(*m))                           # 出力: [[1, 5], [2, 6], [3, 7]]

enumerate関数はシーケンスから(index, value)タプルを得るために用いる。

d = {}
for i, v in ['a', 'b', 'c']:
    d[v] = i
  
# 出力: {'a': 0, 'b': 1, 'c': 2}。

zip関数は複数のシーケンスからタプルのリストを作る。 リストの要素数は最短のシーケンスに合わせる。

s1 = [1, 2, 3]
s2 = ['a', 'b', 'c']
s3 = ['A', 'B']
list(zip(s1, s2, s3))   # 出力: [(1, 'a', 'A'), (2, 'b', 'B')]。

zip関数は、zip化されたシーケンスをunzip(分解)するために用いることもできる。 下例のように、*をリストやタプルの前に置くとリストやタプルは分解されて、1つの引数として使用される。

s1 = 'apple', '300'
s2 = 'banana', '120'
s  = [s1, s2]             # s: [('apple', '300'), ('banana', '120')]。
items, prices = zip(*s)   # items:  ('apple', 'banana')。 prices: ('300', '120')。

集合

集合( set )は重複しない順不同の要素からなる集合である。 主たる用途は存在判定および重複要素の除去である。

集合を作成する。

{1, 2, 3}          # 出力: {1, 2, 3}
set([1, 2, 3])     # 出力: {1, 2, 3}

集合演算に対応している。 大きな集合を処理する場合を想定して、全ての集合演算には左辺値を変更するものが用意されている。

a = {1, 2, 3}
b = {3, 4}
a | b            # 出力: {1, 2, 3, 4}。
a.union(b)       # 出力: {1, 2, 3, 4}。
a |= b           # a: {1, 2, 3, 4}。
b.issubset(a)    # 出力: True。
{1, 2} == {2, 1} # 同等性の判定。出力: True。
集合演算
関数 代替表現 説明
a.add(x) N/A 要素xを集合aに追加する。
a.clear() N/A 集合aを空にする。
a.remove(x) N/A 集合aから要素xを削除する。
a.pop() N/A 任意の要素を集合aから削除する。集合aが空になるとKeyErrorを発生する。
a.union(b) a | b aとbの和集合。
a.update(b) a |= b 集合aを変更し、aとbの和集合にする。
a.intersection(b) a & b aとbの積集合。
a.intersection_update(b) a &= b 集合aを変更し、aとbの積集合にする。
a.difference(b) a - b aとbの差集合。
a.difference_update(b) a -= b 集合aを変更し、aとbの差集合にする。
a.symmetric_difference(b) a ^ b aとbの対称差。
a.symmmetric_difference_update(b) a ^= b 集合aを変更し、aとbの対象差にする。
a.issubset(b) N/A aの要素がbに全て含まれればTrueを返す。
a.issuperset(b) N/A bの要素がaに全て含まれればTrueを返す。
a.isdisjoint(b) N/A aとbに共通要素が一つもないときTrueを返す。

{expression for value in collection if condition}のように内包表記できる。

辞書

辞書( dict )はkey-valueペアの集合であり、他の言語でも「連想配列」、「ハッシュ」などの名称で存在することがある。 keyはimmutableオブジェクトでなければならない。 辞書はkeyの唯一性を条件とすることから「key:value」対によるソートされない集合であると考えられる。

辞書は{key1: value1, key2: value2, ...}の形式で指定して作ることができる。 keyにはkeysメソッド、valueにはvaluesメソッドを用いてアクセスすることができる。 リストやタプルと同様の方法で要素の参照、設定、削除などを行える。

d = {0, 'a', 1: 'b'}           # d: {0: 'a', 1: 'b'}。
d[2] = 'c'                     # d: {0: 'a', 1: 'b', 2: 'c'}。  
1 in d                         # 出力: True
list(d.keys())                 # 出力: [0, 1, 2]。
list(d.values())               # 出力: ['a', 'b', 'c']。
del d[2]                       # d: {0: 'a', 1: 'b'}。
d.update({0: 'A', 2: [0, 1]})  # マージ・更新。 d: {0: 'A', 1: 'b', 2: [0, 1]}。
d[2].append(2)                 # d: {0: 'A', 1: 'b', 2: [0, 1, 2]}。

例外を発生させることなく要素を抽出するにはget関数を用いる。 d.get(k[, x])の形式で使用し、もしd内にkがあればd[k]を返し、なければxを返す。 第二引数を省略した場合はNoneを返す。 d.pop(k[, x])を使用した場合は要素を抽出後、要素を削除する。 辞書から要素を削除する場合はdel文を使用する。

2つのシーケンスから辞書を作る。

d = {}
for k, v in zip(k_list, v_list):
    d[k] = v

collectionsモジュールのdefaultdictクラスを用いれば初期化を引数に指定した関数に従い実行する。

from collections import defaultdict
l = ['a1', 'b1', 'b2', 'a2']
d = defaultdict(list)
for v in l:
    d[v[0]].append(v)

# d: defaultdict(<class 'list'>, {'a': ['a1', 'a2'], 'b': ['b1', 'b2']})

{key-expression:value-expression for value in collection if condition}のように内包表記できる。

l = ['cat', 'lion', 'tiger']
{v:i for i,v in enumerate(l)}   # 出力: ['cat': 0, 'lion': 1, 'tiger': 2]。
辞書のitemsメソッドによりkeyとvalueを同時に取得できる。
d = {'a':1, 'b':2}
d.items()            # 出力: dict_items([('a', 1), ('b', 2)])
for k,v in d.items:
    print(k, v)

# 出力:
#   a 1
#   b 2

関数

関数の定義

関数はdef文で宣言する。 関数からはreturn文または関数末尾に達した場合に復帰する。 return文には復帰時に返す値を指定する。 複数の返り値を指定してもよい。 return文がない場合はNoneを返す。

関数の引数

位置引数および「key = value」形式のキーワード引数がある。 可変長引数は仮引数に「*変数名」を配置することで可能であり、これによる引数はタプルにまとめられる。 「*変数名」は位置引数後に配置し、以後はキーワード引数のみ配置することができる。 同様に仮引数に「**変数名」を配置することで可変長の辞書型引数をとることができる。

def hoge(*args, sep="/"):
    return sep.join(args)

hoge('a', 'b', 'c')     # 出力: a/b/c

*演算子を用いて関数を呼び出すことでリストやタプルをアンパックした引数を渡すことができる。 同様に**演算子を用いて辞書をキーワード引数にして渡すことができる。

l = [2, 5]
list(range(*l))    # 出力: [2, 3, 4]

関数を他の関数の引数として使用することもできる。

i = list(range(10))
j = [mean, min, len]
def hoge(data, functions, offset = 1):
    result = []
    for f in functions:
        value = f(data) + offset
        result.append(value)
    return result

hoge(i, j)     # 出力: [10, 1, 11]。
result         # 出力: NameError。関数内で定義されたvalue, f, result変数は関数実行後消滅するため。

関数のスコープ

関数の引数にオブジェクトを渡すと、そのオブジェクトに対する参照として新たなローカル変数が作られる。 また関数の中で新しいオブジェクトを作成して変数名を付けたとしても、その操作は関数の呼び出し元である親のスコープには影響を与えない。

無名関数

Pythonは無名(lambda)関数をサポートする。

def hoge(l, f):
    return [f(x) for x in l]

hoge(list(range(5)), lambda x: x * 2)   # 出力: [0, 2 , 4, 6, 8]。

引数の部分適用

引数を部分適用することで、既存関数から新しい関数を導出すること(カリー化)ができる。 functoolsモジュールにあるpartial関数を用いてこの手順を簡素化することができる。

def hoge(i, j):
    return i + j * 2

from functools import partial
hoge_i = partial(hoge, 1)
hoge_j = partial(hoge, j = 1)

hoge_i(2)         # 出力: 5。 hoge(1, 2)。
hoge_j(2)         # 出力: 4。 hoge(2, 1)。

関数のメタ情報

関数本体の最初の文に文字列リテラルを使用した場合、これを関数のドキュメント文字列(docstring)といい、__doc__属性に格納される。 docstringの形式は慣習として1行目を簡潔な要約、それ以降続きがある場合は2行目は空行、3行目以降に説明を記述する。 関数の型についてのメタ情報として関数注釈を含めることもでき、これは__annotation__属性に格納される。 引数に対する注釈は引数名の後にコロン、式と続けることで定義し、返り値に対する注釈はdef文末のコロンの直前に->と式を配置することで定義する。

def hoge(a:str, b: int, c: str = 'banana') -> str:
    """Price

    Show the total price of items 
    """
    print("The price of", a, "and", c, "is", str(b), "USD", end=" ")


hoge("apple", 100)            
# 出力: The price of apple and banana is 100 USD

print(hoge.__doc__)
# 出力: Price
#
#           Show the total price of items 
		    
print(hoge.__annotations__)   
# 出力: {'a': <class 'str'>, 'b': <class 'int'>, 'c': <class 'str'>, 'return': <class 'str'>}

例外処理

エラーには構文エラー(syntax error)と例外(exception)がある。 例外はプログラム実行中に検知されるエラーである。 例外には様々な種類の型があり、組込みの例外一覧は こちらから確認できる。

try節を実行時にエラーが発生した場合にexcept節を実行する。 else節やfinally節を含めることもでき、else節はtry節が正常に実行された場合にのみ実行される。 一方、finally節はtry節の結果や途中のreturnの有無などの如何によらず必ず実行される。 これはクリーンアップ動作を定義することを意図したもので、実際のアプリケーションでは外部リソース(ファイルや外部ネットワークのコネクション)を開放する等の目的で利用される。 "except TypeError:"や"except (TypeError, ValueError)"のように記述することでexcept節で応答する例外型を指定することができる(無指定では全ての例外型)。

def hoge(x):
    try:
        x = int(x)
    except:
        print('err')
    else:
        print('success')
        return(x)
    finally:
        print('bye')

hoge(2)     # 出力: '2'を返すとともに、'success'と'bye'が画面出力される。 returnの配置に注意が必要。
hoge('t')   # 出力: 'err'と'bye'。

except節では例外の後ろに変数指定できる。 この変数には例外のインスタンスが結合され、引数はargs属性に格納される。 以下の例ではraise文により指定の例外を強制的に発生させている。

try:
    raise Exception('a', 'b')
except Exception as e:
    x. y = e.args
    print(x, y)                 # 出力: a, b

現在処理中の例外についての情報を得るためにはsys.exc_info関数を使用する。

import sys
try:
    0/0
except:
    a, b, c = sys.exc_info()
    print('Name:', a.__name__)
    print('Msg:', b)
    print('Line:', c.tb_lineno)

# 出力: Name: ZeroDivisionError
# 出力: Msg: division by zero 
# 出力: Line: 2

例外クラスを作成することで独自の例外を追加することが可能である。 このとき通常は、例外はExceptionクラスから派生したクラスとするべきである。

ファイル操作

ファイルの読み書き

open関数はopen(filePath,mode)の形式で使用し、ファイルハンドル(ファイルへの参照)を返す。 モードの指定を省略した場合は読み込み専用モード'r'とみなされ、既定ではテキストモードで開かれる。 テキストモードでは既定の設定として、読込みの際にはプラットフォーム依存の行末文字(Windowsの\r\nなど)は\nに変換され、書込みの際にはこの\nはプラットフォームごとの行末文字に変換される。 for文を利用することで各行を逐次処理することができ、各行は改行コードを含むため、rstripメソッドで除去する。 開いたファイルはcloseメソッドで閉じることでリソースをOSに開放する。

l = [x.rstrip() for x in open(path)]
f.close()

with文を使用すれば、with節実行後に自動的にファイルが閉じる。

with open(path) as f:
    l = [x.rstrip() for x in f]
ファイルモード
モード 説明
r 読み込み専用モード。
w 書き込み専用モード。新規ファイル作成。同名ファイルが存在すればデータを消す。
x 書き込み専用モード。新規ファイル作成。同名ファイルが存在するときは失敗する。
a 既存のファイルに追記する。ファイルが存在しない場合は作成する。
r+ 読み込みと書き込みを両方行う。
b バイナリファイルモード。'rb'や'wb'のように指定する。
t テキストモード(既定)。
ファイルメソッド
read([size]) ファイルデータを文字列として戻す。size引数を指定した場合は読込みバイト数を指定でき、無指定だとファイル全体を読込む。
readline() ファイルの各行をリスト形式で戻す。
write('文字列') 文字列をファイルに書き込む。
writelines() 文字列のシーケンスをファイルに書き込む。
close() ファイルオブジェクトを閉じる。
flush() 内部のI/Oバッファーをディスクに書き込む。
seek(pos) 整数で指定したファイル位置に移動する。
tell() 現在のファイル位置を整数で返す。

シリアル化

JSONによりint, float, str, list, dict, True, False, Noneをシリアル化することができる。 JSONによるシリアル化処理の基本形式は以下の通りである。

import json

# 書込み
with open(path, "w") as fh:
    json.dump(obj, fh)

# 読込み
with open(path, "r") as fh:
    obj = json.load(fh)

pickleはJSONと異なり任意のPythonオブジェクトをシリアライズできるが、他の言語で書かれたアプリケーションとの通信には使用できない。

import pickle

# 書込み
with open (path, 'wb') as fh:
    pickle.dump(obj, fh)

# 読込み
with open(path, "rb") as fh:
    obj = pickle.load(fh)

読書き以外のファイル操作

その他のファイル操作に関してはos, shutilモジュールから提供されている。

import os
os.getwd()                   # pwdコマンドに対応。
os.chdir(dir)                # cdコマンドに対応。
os.listdir(dir)              # lsコマンドに対応。
os.path.isdir(path)          # [ -d path ]に対応。
os.path.isfile(path)         # [ -f path ]に対応。
os.path.exists(path)         # [ -e path ]に対応。
os.remove(path)              # rmコマンドに対応。
os.mkdir(path)               # mkdirコマンドに対応。

d = '/a/b'
f = 'c.fa'
p = os.path.join(d, f)              # p: '/a/b/c.fa'。
os.path.split(p)                    # 出力: ('/a/b', 'c.fa')
os.path.splitext(p)                 # 出力: ('/a/b/c', '.fa')
os.path.dirname(p)                  # 出力: '/a/b' 
os.path.basename(p)                 # 出力: 'c.fa'

import shutil
shutil.move(src, dest)        # mvコマンドに対応。
shutil.copy.(src, dest)       # cpコマンドに対応。
shutil.copytree.(src, dest)   # cp -rに対応。

モジュール

モジュールの実行

モジュールとは、拡張子.pyをもつPythonコードファイルを指す。 モジュールはpython moduleName.py argsの形式で実行することができる。

モジュールのインポート

今、hoge.pyを次のように作成したものとする。

PI = 3.14
def f(x):
    return x + 1

モジュールは他のモジュールから利用することができ、以下の例ではimport側のシンボル表にはモジュール名が入る。

import hoge
hoge.f(1)     # 出力: 2
hoge.PI       # 出力: 3.14
f = hoge.f
f(1)          # 出力: 2

モジュール内で定義されている名前をimportする側のシンボル表に取込むこともできる。

from hoge import f, PI
PI            # 出力: 3.14
f(1)          # 出力: 2

モジュール及び現在定義されている名前を確認するにはdir関数を用いる。

import sys
dir(sys)            # sysモジュールで定義されている名前の一覧。
dir()               # 現在定義されている名前の一覧。ビルドイン以外の変数、モジュール、関数などの名を含む。
dir(__builtins__)   # ビルドインのものの一覧。

コンパイル済みモジュールファイル

モジュール読み込みの高速化のため、Pythonはコンパイル済みモジュールを__pycache__ディレクトリにmoduleName.versionName.pycの名前でキャッシュする。

パス解決

モジュールがインポートされる際、まずビルドインモジュールの名前が検索され、見つからなければsys.path変数でリスト化されたディレクトリの中を検索する。sys.pathによるパス解決順は以下の通りである。

sys.pathを確認する。

import sys
sys.path

PYHTONPATHを設定する(Unixシェルにおいて)。

PYTHONPATH="/path/to/target" python3    # python起動後sys.pathで確認する。

モジュールがどのファイルから読込まれたかは__file__属性から確認できる。

import hoge
hoge__file__          # パスが出力される。

パッケージ

複数のモジュールをまとめてパッケージを構築することができる。 以下の構成を想定し、たとえばc11.pyには対応する番号のf11()が定義してあるものとする。

P
├── C1
│   ├── __init__.py
│   ├── c11.py
└── __init__.py

ディレクトリ内にパッケージを含むものとして処理させるためには__init__.pyファイルを必要とする。 通常は空ファイルであるが、パッケージの初期化処理を実行することもできる。

モジュールの読込みは以下のように行うことができる。

# case1
import P.C1.c11
P.C1.c11.f11()

# case2
from P.C11 import c11
c11.f11()

# case3
from P.C11.c11 import f11
f11()

標準ライブラリ

標準ライブラリはPythonインストーラーとともに配布されており、多種多様なモジュール群を追加インストールなしに利用することができる。 こちらからそのモジュール群の一覧を確認することができる。

クラス

クラスの定義

独自のクラスを定義することで既存のデータ型では十分に処理できないデータをモデル化する。

クラスはオブジェクトを生成するための鋳型である。 クラスを作成するための基本構文は以下の通りである。

class className:
    body

bodyに入ると新しい名前空間が生成されローカルスコープとして使用される。 通常は一連の関数や変数が定義される。 クラス定義から正常に抜けるとクラスオブジェクトが作られ、これはクラス定義で作られた名前空間の内容を包むラッパーとなる。

クラスオブジェクトは属性参照とインスタンス化の操作に対応している。 属性参照は他の場合と同様obj.nameの形式を採る。 クラス属性への代入も可能である。

class hoge:
    i = 1

hoge.i         # 出力: 1  
hoge.i = 2
hoge.i         # 出力: 2

インスタンスはクラスの実体である。 クラスのインスタンス化には関数表記が使用される。 __init__という特殊メソッドが定義されている場合、新規生成するインスタンスに対して自動的に__init__()が呼出されるため、インスタンスの初期状態をカスタマイズすることができる。 selfはclassNameのインスタンスそのものを表すために使用される変数であり、別の変数名も使用可能だが、慣例によりこの名称が使用される。 インスタンスオブジェクトは属性参照に対応している。 属性名としてはデータ属性とメソッドが有効である。 インスタンスのメソッドはクラス属性の中で関数オブジェクトであるものに対応する。 メソッドでは第一引数としてインスタンスのオブジェクトが渡されるため、i.f()の呼出しはhoge.f(i)と等価である。 メソッドは別のメソッドを呼出すことも可能である。

class hoge:
    def __init__(self):
        self.data = []
    def f(self):
        return 'foo'

i = hoge()    # インスタンス化
i.data        # 出力: []
i.f()         # 出力: 'foo'

クラス変数はあるクラスの全てのインスタンスが共有する属性やメソッドであり、インスタンス変数はインスタンス固有の属性やメソッドである。

class cat:
    kind = 'Felis catus'              # クラス変数
    def __init__(self, name):
        self.name = name              # インスタンス変数

mike = cat('mike')
tora = cat('tora')

mike.name     # 出力: 'mike'
tora.name     # 出力: 'tora'
mike.kind     # 出力: 'Felis catus'
tora.kind     # 出力: 'Felis catus'

特殊メソッド

特殊メソッドは特定の条件時に実行される。

__len__メソッドは関数len(インスタンス)が呼出し時に実行される。 len関数の動作を設定する場合には__len__を定義する必要がある。 __str__メソッドはstr(obj)またはprint(obj)で呼出される。 その他にも、__repr__、__getitem__、__iter__、__setitem__などがある。

継承

基底クラスから派生クラスを定義する構文は次の通りである。

class DerivedClassName(BaseClassName):
    Block

組込み型から新たなデータ型を作成した例を示す。

class zdict(dict):
    def __missing__(self, x):
        return 0

a = zdict()
b = dict()
a['a']       # 出力: 0
b['a']       # 出力: KeyError

多重継承も可能である。

class DerivedClassName(Base1, Base2, ...):
    Block

親クラスから継承した属性の探索順序は、探索対象名がDerivedClassNameになければ次はBase1を、Base1にもなければBase1の基底クラスを再帰的に探索し、そこにもなければBase2を...、という順序になる。 単純な場合は以上の通りだが、super()の協調コールに対応するためにメソッド解決順が動的に変化する場合もある。

インスタンス型はisinstance関数を用いて確認できる。 クラス継承はissubclass関数を用いて確認できる。

class hoge:
    pass
class foo:
    pass
i = hoge()
j = foo()

isinstance(i, hoge)     # 出力: True
isinstance(j, hoge)     # 出力: True
isinstance(j, foo)      # 出力: True
issubclass(foo, hoge)   # 出力: True 

プライベート変数

オブジェクト内部からのみアクセスを許すプライベート変数はPythonには存在しない。 ただし、慣習としてアンダースコアが前置された名前はAPI非公開とされている。 またサブクラス化されるよう設計されたクラスの属性名が名前衝突を起こすことを避けるために名前修飾(name mangling)という機構が存在する。 これは__spam_のように、アンダースコアが2つ以上前置され、後置されたアンダースコアが1つ以下のものを全て__classname__spamの形に字句的に置換し、クラス固有の名前にすることをいう。

ジェネレータ

イテレータプロトコルはオブジェクトの逐次処理を可能にする汎用的な方法であり、イテレータを生成する手法の一つがジェネレータである。 ジェネレータは一連の複数の結果を遅延しながら戻すことができる。 ジェネレータを作るには関数内で、returnの代わりにyieldキーワードを使用するか、リスト内包表現において[]でなく()で囲む。

g = (x**2 for i in range(5))   # g: <generator object <genexpr> at 0x7f45b4564fe0>
sum(g)               # 出力: 30。

reversed関数はシーケンスを逆順処理するためのジェネレータである。

list(reversed(range(5)))    # 出力: [4, 3, 2, 1, 0]。

itertoolsライブラリには多くのジェネレータがある。

l = ['a1', 'a2', 'b1', 'b2', 'a3']
import itertools
for i, v in itertools.groupby(l, lambda x: x[0]):
    print(i, list(v))

#  出力:
#    a ['a1', 'a2']
#    b ['b1', 'b2']
#    a ['a3']
説明
combinations(iterable, k) 可能なk個の要素を持つタプルをiterableの中から生成する。 この際、並び順が異なっていても組み合わせが同一であれば同一とみなし、復元抽出はしない。
permutations(iterable, k) 可能なk個の要素を持つタプルをiterableの中から生成する。この際、要素の並び順も考慮する。
groupby(iterable, func) (キー、キーごとのグループを参照するイテレータ)形式のタプルを各キーごとに生成する。
product(*iterable, repeat=1) 入力した変数iterablesの直積をタプルとして生成する。ネストしたfor文と同様な処理が可能になる。

for文はシーケンスにiter()を呼び出すようにする。 この関数は反復子オブジェクトを返し、反復子オブジェクトにはシーケンス要素にひとつずつアクセルする__next__()メソッドが定義してある。 要素が尽きると__next__()はStopIteration例外を送り、forループはこれをうけて終了する。 __next__()メソッドはビルトイン関数next()を呼出すことで呼出すことができる。

i = iter('abc')
next(i)            # 出力: 'a'
next(i)            # 出力: 'b'
next(i)            # 出力: 'c'
next(i)            # 出力: StopIteration

自作クラスに反復子の挙動を追加するには、__next()__メソッドのついたオブジェクトを返す__iter__()を定義すればよい。 すでに__next__()が定義してあるクラスでは__iter__()はselfを返すだけでよい。

class Reverse:
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

rev = Reverse('hoge')
for i in rev:
    print(i, end = ",")     # 出力: e,g,o,h

演習課題

SAM形式ファイルの各FLAGの総数を出力するスクリプトを作成せよ。

d = {}
with open(file) as f:
    for l in f:
        l = l.rstrip()
        if l.startswith('@'):
            continue

        a = l.split('\t')
        flag = a[1]
        if flag in d:
            d[flag] += 1
        else:
            d[flag] = 1

for k, v in d.items():
    print(k, v)

fastaクラスを定義せよ。

class Fasta:
    def __init__(self, id, desc, seq):
        self.id   = id
        self.desc = desc
        self.seq  = seq

    def gc(self):
        g  = self.seq.upper().count('G')
        c  = self.seq.upper().count('C')
        gc = (g + c) / len(self.seq)
        return gc

    def __len__(self):
        return len(self.seq)

    def __repr__(self):
        return f''

@staticmethod
    def parse(file_name):
    def parse_title(title):
        title = title.strip('>\n ')
        if ' ' in title:
            seq_id, desc = title.split(' ', 1)
        else:
            seq_id, desc = title, ''
        return seq_id, desc

with open(file_name) as f:
    line = next(f)
    seq_id, desc = parse_title(line)
    seq = ''
    for line in f:
        if line.startswith('>'):
            yield Fasta(seq_id, desc, seq)
            seq_id, desc = parse_title(line)
            seq = ''
        else:
            seq += line.strip('\n')
            yield Fasta(seq_id, desc, seq)


#@
f_list = list(Fasta.parse('data.mfa'))
f_list