前回に続いて、関数のパートで楽しめたとこをノートしていく。

関数返り値にNoneを設定するのはヤメて、例外を飛ばせ。

try:
    return a/b
except ZeroDivisionError as e:
    raise ValueError('えらぁだよ') from e

てかこうやってエラーの種類を変えられるの知らなかったわ。

Pythonはクロージャをサポートしてる。

クロージャってのは、定義されたスコープの変数を参照する関数のこと。ああ、関数の中から外の変数を使えるもんね。アレのことか。

タプルの比較規則。

(0, b) (1, a) の場合、まず[0]の0と1、そのつぎに[1]のbとaの比較になる。

nonlocal宣言でローカル変数の参照渡しみたいなことができる。

def foo():
    a = 'fooのa'

    def bar():
        nonlocal a
        a += 'なぁんつって'

    bar()
    print(a)  # fooのaなぁんつって

foo()

こういうことか。aがグローバル変数だった場合 nonlocal じゃなくて global になるところが注意。

リストを作成して返す関数は、もしかしたらジェネレータ関数にできるんじゃない?

やり方は lis.append(x) していたところを yield x にするだけ。これはかなりいいのでは? プログラミング問題とかでよくでかいリストを返す関数を作ってたけど、あれは全部エコなジェネレータにできるってことか。

可変長位置引数で見た目をすっきりさせよう。

def foo(message, *args):

こういうやつね!

foo('メッセージ', 1, 2, 3, 4)

って呼び出せば、1以降がリストになって args に代入される。……ん? いや別に、最初からリストで渡せばよくね?

ちなみにすでにリストになってるなら

foo('メッセージ', *list)

って渡せる。いや、最初からリストでよくね???

可変長位置引数を使うとき注意しないといけないことがあって、たとえば上の foo を

foo(message, message2, *args)

に定義変更しても呼出でエラーは起こらない。これは発見困難バグを生むのでやばい。さらに、これにジェネレータを渡すと勝手にタプルにするからメモリ爆発することがある。いやメンドくせーよ! 最初からリストで渡せばいいだろ!

キーワード引数、デフォルト引数は良い。

def foo(message, option1=1, option2=2):

見るからに使い勝手いい。

デフォルト引数には罠がある。

def foo(message, when=datetime.now()):

こうするとwhenのデフォルト値は定義時の時間で固定されちゃう。fooを呼び出すたびにその時間を取得してほしいなら、デフォルト引数Noneを使う。

def foo(message, when=None):
    when = datetime.now() if when is None else when

こんな罠もあるよ♪

def foo(x, dic={}):
    dic[x] = 1
    return dic

print(foo('a'))  # {a:1}
print(foo('b'))  # {a:1, b:1}  クソワロタww

定義時にひとつのディクショナリが定義されちゃってて、以降デフォルト引数にはそのディクショナリオブジェクトが使われ続けるので、こーゆーことになる。これも上と同じで None を使う。

キーワード専用引数。

これはすげぃ。

def foo(a, b, *, option1=False, option2=False):

こうすると、option1以降の引数はキーワードでしか指定できなくなる。完全にオプション専用の引数ができるってことじゃん。 Python2ではこの構文がないのだけど、同じ動作を作ることはできる。

def foo(a, b, **kwargs):
    option1 = kwargs.pop('option1', False)
    option2 = kwargs.pop('option2', False)
    if kwargs:
        raise TypeError('Unexpected **kwargs')

今回のフェイバリットは最後のキーワード専用引数かな。すげえ便利そう。

Effective Python 感想文一覧