More Control Flow Tools

昨日の続きです。

More on Defining Functions

Default Argument Values

まずはデフォルト引数について。App EngineのAPIを見ているとよく見かけます。引数が指定されなかった時に自動的に割り振ってくれる機能です。

>>> def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
...     while True:
...         ok = raw_input(prompt)
...         if ok in ('y', 'ye', 'yes'):
...             return True
...         if ok in ('n', 'no', 'nop', 'nope'):
...             return False
...         retries = retries - 1
...         if retries < 0:
...             raise IOError('refusenik user')
...         print complaint
...
>>> ask_ok('Do you really want to quit?')
Do you really want to quit?y
True
>>> ask_ok('Do you really want to quit?', 2)
Do you really want to quit?d
Yes or no, please!
Do you really want to quit?d
Yes or no, please!
Do you really want to quit?d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in ask_ok
IOError: refusenik user
>>> ask_ok('Do you really want to quit?', 2, 'Come on only yes or no!')
Do you really want to quit?d
Come on only yes or no!
Do you really want to quit?yes
True


ここで重要なのが、デフォルト引数がいつ評価されるかです。たとえばこんなコードを書いてみます。

>>> i = 5
>>> def f(arg=i):
...     print arg
...
>>> i = 6
>>> f()
5


なるほど、引数のデフォルト値は関数の定義をインタープリタが見つけた時点の値で初期化するようです。デフォルト引数を変更可能な (Mutable) オブジェクトにするとさらに複雑になります。

>>> def f(a, L=[]):
...     L.append(a)
...     return L
...
>>> print f(1)
[1]
>>> print f(2)
[1, 2]
>>> print f(3)
[1, 2, 3]


なるほど、引数Lは一度だけ初期化されて後は使い回されるわけですね。

Keyword Arguments

こちらはキーワードで引数を指定する方法です。Smalltalkでは一般的ですよね。

>>> def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it."
...     print "-- Lovely plumage, the", type
...     print "-- It's", state, "!"
...
>>> parrot(1000)    # 最初の引数 voltage をキーワードなしで指定
-- This parrot wouldn't voom if you put 1000 volts through it.
-- Lovely plumage, the Norwegian Blue
-- It's a stiff !
>>> parrot(voltage = 100, state = 'some sticks')    # 第一、第二引数をキーワードで指定
-- This parrot wouldn't voom if you put 100 volts through it.
-- Lovely plumage, the Norwegian Blue
-- It's some sticks !


これまで仮引数の名前にはあまり気を使ってきませんでしたが (C言語では書かなくてもいいし) こういう使い方が出来るのであれば、仮引数の名前もきちんと考えて付けないといけませんね。なお、引数をキーワードで指定する場合は全てキーワードで指定する必要があります。ごちゃ混ぜには指定できません。

>>> parrot(voltage = 100, 'some sticks')
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg


最後に可変長引数とディクショナリ引数について。可変長引数はその名の通り、ディクショナリ引数はキーワード引数のセットです。これらは同時に使うことが出来ますが、可変長引数はディクショナリ引数の前に持ってくる必要があります。

>>> 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
...     keys = sorted(keywords.keys())
...     for kw in keys:
...             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.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch


↑の*argumentsというのが可変長引数です。中身は普通のリストなので順番は指定された順番になります。**keywordsがディクショナリ引数です。でも合わせて使うとよく分からなくなりますね。

Arbitrary Argument Lists

こちらは可変長引数を使って任意個の引数リストを指定する方法が書いてあります。

>>> def multiple_items(separator, *args):
...     print separator.join(args)
...
>>> multiple_items('|', "Nakai", "Kensuke")
Nakai|Kensuke
>>> multiple_items(' | ', "Tokyo", "Higashiakiru", "Ogawa")
Tokyo | Higashiakiru | Ogawa
>>> multiple_items('|')

>>>
Unpacking Argument Lists

こちらはなかなか面白い機能です。ていうか初めて見ました。


組み込みのrange()関数は引数二つをとって、その間の数をリストにして返してくれます。この引数にリストを指定したい場合、次のように指定します。

>>> range(2, 10)
[2, 3, 4, 5, 6, 7, 8, 9]
>>> range(*[2, 10])
[2, 3, 4, 5, 6, 7, 8, 9]


この二つの記法は同じ結果をもたらします。つまり * 演算子をリストに適用すると展開 (Unpaking) してくれるのです。もちろんリストの中にある順に展開されます。これと同じように辞書も展開することが出来ます。

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it.",
...     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 !


辞書の場合は ** 演算子ですね。

Lambda Forms

お次は最近では常識と化したラムダ (λ) 式について。ラムダと書くと分かりにくければ無名関数といいましょうか。ラムダ式を使えば関数をオブジェクトみたいに動的に生成出来るわけです。

>>> def make_incrementator(n):
...     return lambda x: x + n
... 
>>> f = make_incrementator(43)
>>> f(1)
44
>>> f(2)
45
>>> f(47)
90


1足す関数を作っています。ほんと関数がオブジェクトになっています。


そんなラムダ式ですが、たまに使いたくなるんだけど、使うと決まって後から見ると???なんですよね。

Documentation Strings

この章、何だか難しかった。要は関数などのコメントの付け方 (Documentation) に関する説明。

>>> def my_function():
...     """Do noting, but document it.
... 
...     No, really, it doesn't do anything
...     """
...     pass


これが典型的なコメントの付け方です。コメントは上でも書いたように関数名の直後から始めます。


一行目は関数の目的を概要 (Summary) レベルで書きます。一行でコメントが終わる場合はこれで終了。トリプルクォートでコメントを閉じます。二行以上のコメントが書きたい場合、一行空行を入れる必要があります。概要と区別するためだそうです。その後はインデントを合わしてコメントを書いていきます。インデントが少ないとドキュメントを自動生成した時に出てこないそうです。


う〜ん、いまいちピンと来ません。ドキュメントを生成するとピンと来るのかな?ということで試してみました。

#! /usr/bin/python
#! -*- coding: utf-8 -*-
def doc_test():
    """This function is for documentation test.

    How the following line appear?
    """
    pass


Pythonの標準ドキュメント生成ツールはpydocです。試してみましょう。

% pydoc ~/work/python/tutorial/doctest.py
Help on module doctest:

NAME
    doctest - #! -*- coding: utf-8 -*-

FILE
    /home/yohpapa/work/python/tutorial/doctest.py

FUNCTIONS
    doc_test()
        This function is for documentation test.
        
        How the following line appear


文字コード指定が意味不明な場所にありますが、なるほどインデントなどがキレイに揃えられています。インデントを少なくしてみましょう。

#! /usr/bin/python
#! -*- coding: utf-8 -*-
def doc_test():
    """This function is for documentation test.

    How the following line appear?
This line disapper?
    """
    pass
% pydoc ~/work/python/tutorial/doctest.py
Help on module doctest:

NAME
    doctest - #! -*- coding: utf-8 -*-

FILE
    /home/yohpapa/work/python/tutorial/doctest.py

FUNCTIONS
    doc_test()
        This function is for documentation test.
        
                How the following line appear?
        This line appear?


あれれ?出ますね。でもインデントが狂っています。まぁ、インデントは合わせろってことですかね。

Intermezzo: Coding Style

ここではコーヒーブレイク (Intermezzo: 間奏) としてPythonのコーディングスタイルに触れています。PythonではPEP 8というスタイルが一般的だそうです。PEP 8のうちのいくつかをピックアップします。

  • インデントにはTABを使うな、4つのスペースを使え (!)
  • 1行79文字を越えてはいけない
  • 可能であれば各行の右側にコメントを書こう (!)
  • 上で書いたドキュメンテーション用のコメントを使え
  • 演算子の両側、カンマの次にスペースを一つ入れろ
  • クラスにはCamelCrach、関数にはnever_give_upという法則で名前を付けろ
  • コードの文字コードはASCIIがベスト


(!) を書いたところはちょっと合わないところです。多分タブを使うだろうし、コメントは右側に書かないと思う。