前回は、Pythonの関数の基本を取り上げた。今回は関数のより進んだ使い方を取り上げる。全てを理解する必要はないが、他人の書いたコードやライブラリを理解するために、「こういった書き方や使い方ができる」ということは知っておきたい。
前回は関数の引数にデフォルト値を指定する方法まで紹介した。その場合の引数は任意指定が可能だったが、指定する位置は関数で定義した引数の位置の順番のまま記述する必要があった。Pythonでは「キーワード引数」という書き方が可能で、次のように引数にキーワードを指定すれば順序を入れ替えても引数を指定することができる。
>>> def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
... print("ーー This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.")
... print("ーー Lovely plumage, the", type)
... print("ーー It's", state, "!")
...
>>>
>>> parrot(1000) # 1 positional argument
ーー This parrot wouldn't voom if you put 1000 volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's a stiff !
>>> parrot(voltage=1000) # 1 keyword argument
ーー This parrot wouldn't voom if you put 1000 volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's a stiff !
>>> parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
ーー This parrot wouldn't VOOOOOM if you put 1000000 volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's a stiff !
>>> parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
ーー This parrot wouldn't VOOOOOM if you put 1000000 volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's a stiff !
>>> parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
ーー This parrot wouldn't jump if you put a million volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's bereft of life !
>>> parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
ーー This parrot wouldn't voom if you put a thousand volts through it.
ーー Lovely plumage, the Norwegian Blue
ーー It's pushing up the daisies !
>>>
上記サンプルではポジション引数とキーワード引数の双方が組み合わされて使われている。ちょっとわかりにくいかもしれないが、この辺りは”何となく”で感覚的に理解できると思う。
キーワード引数は、うまく使えばマニュアルを読まなくても関数の動作が理解できるようになる。次に示すのは、キーワード引数を誤って使った場合のサンプルで、Pythonインタプリタがエラーを出力している。
>>> def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
... print("ーー This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.")
... print("ーー Lovely plumage, the", type)
... print("ーー It's", state, "!")
...
>>> parrot() # required argument missing
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: parrot() missing 1 required positional argument: 'voltage'
>>> parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> parrot(110, voltage=220) # duplicate value for the same argument
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: parrot() got multiple values for argument 'voltage'
>>> parrot(actor='John Cleese') # unknown keyword argument
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: parrot() got an unexpected keyword argument 'actor'
>>>
Pythonでは引数指定を任意数取る方法として「*v」や「**v」といった指定方法も提供している。「*v」は任意数の引数、「**v」はディレクトリを引数として取ることを表す。次に示すのは、「*v」と「**v」の使用例だ。それぞれfor制御構文で中身を取り出して任意数を処理している点に注目してほしい。
>>> def cheeseshop(kind, *arguments, **keywords):
... print("ーー Do you have any", kind, "?")
... print("ーー I'm sorry, we're all out of", kind)
... for arg in arguments:
... print(arg)
... print("-" * 40)
... for kw in keywords:
... print(kw, ":", keywords[kw])
...
>>> cheeseshop("Limburger", "It's very runny, sir.",
... "It's really very, VERY runny, sir.",
... shopkeeper="Michael Palin",
... client="John Cleese",
... sketch="Cheese Shop Sketch")
ーー Do you have any Limburger ?
ーー I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch
>>>
通常、任意数の引数は引数の最後に書くことになっている。つまり上記サンプルのように通常の引数を書いてから、「*v」「**v」の順に指定する。ただし、次のように「*v」の後にキーワードのみの指定であれば書くこともできる。
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
>>>
次に示すのは、関数の引数に任意数の引数を書く場合に、すでにリストやタプル変数にデータが保持されている場合の例だ。リストの場合は、例えばリストを「range(*v)」のように書けばよい。タプルの場合には、「**v」のように書けばよいとされている。
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("ーー This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
ーー This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
>>>
この辺りから、少々理解が難しくなってくるかもしれない。サンプルコードを読んで動作がよくわからなかったら、とりあえず「こういった書き方をするものなんだ」ということを理解しておいてもらえればと思う。
次に挙げる2つの例は「ラムダ(無名関数)」を使った書き方だ。ただし無名関数は少々厄介なので、サンプルコードが理解できなくても良いのではないかと思う。
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(0)
42
>>> f(1)
43
>>> f(1)
43
>>>
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
>>>
無名関数は「lambda 引数:」のように記述する。上記例もこのルールに従って書いてあることがわかれば、ある程度プログラミングの経験のある方なら動作の予測がつくのではないだろうか。自分で書くときにあえて無名関数を使う必要はないが、「こういった書き方をすることがある」ということは知っておいていただきたい。
次は関数の説明を記述する「ドックストリング」を書いた例だ。「”“”」で囲んで指定し、最初の行に関数の動作を1行で書くことが推奨されている。1行目はピリオドで終わるのがよしとされている。
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
>>>
関数のさらに詳しい説明は3行目以降に書くことになっている。3行目以降に書く場合には2行目は空行にする。関数としては、この辺りの書き方を一通り網羅しておきたい。
コーディングスタイル
Pythonは人気の高いほかのプログラミング言語と比べると誰が書いても同じようなコードになりやすいが、それでもかなりの自由度がある。Pythonのチュートリアルでは、関数まで取り上げた段階で簡単なコーディングスタイルについての説明が記されている。説明されているのは次のようなルールで、このルールに従って記述することで誰が書いても似たようなコードにしようというものだ。
- インデントには4文字を空白を使用する。タブは使わない
- 1行は79文字を超えないように折り返す
- 関数、クラス、関数内の大規模ブロックは空行を使って分ける
- 可能であればコメントは1行に書く
- ドックストリングを書く
- コロンの後と演算子の前後には空白を挟む、カッコの内側には空白を挟まない
- クラス名と関数名表記には一貫したネーミングルールを適用する。例えばクラスはCamelCase、関数名やメソッドにはlower_case_with_underscoresのように書く。メソッドの第1引数はselfで参照する
- 国際環境での使用を考慮するならまれなエンコーディングの使用は避ける(PythonではUTF-8がデフォルト)
- 異なる言語を使う方がメンテナンスやコードリーディングを実施する可能性があるのであれば、識別子にはASCIIを使うようにする
これらは「必ずこう書かなければならない」というものではないのだが、Pythonのコードは推奨されているコーディングスタイル”風”に書かれていることが多い。他人の書いたコードの理解を早めるためにも、自らもこの書き方に習っておいたほうが何かと便利だ。このルールを参考にしてコーディングしてみてほしい。
・ The Python Tutorial