初めてのPythonを読んでみる(8)
- 13章 スコープと引数
スコープには以下の3つがある。
- グローバルスコープ ... モジュールのトップレベル
- ローカルスコープ ... 関数の呼び出し単位
- ビルトインスコープ ... 言語組み込みのスコープ。 __builtin__ で定義されている変数が所属
>>> x = 1 >>> def func(): ... x = 10 ... def innerfunc(): ... x = 100 ... print x ... innerfunc() ... print x ... >>> func() 100 10
変数に代入が行われた時点で、ローカルスコープに属する変数となる。※オブジェクトの上書きと代入は別。
>>> L = [] >>> def func(): ... L.append(1) ... L.append(2) ... >>> L [] >>> func() >>> L [1, 2]
>>> L = [] >>> def func(): ... X = [1,2,3] ... L = X ... >>> L [] >>> func() >>> L []
>>> L = [] >>> def func(): ... L[:] = [1,2] ... >>> L [] >>> func() >>> L [1, 2]
LEGBルール (local, enclosing, global, builtin) と呼ばれるものがある。Pythonのスコープに関するルール。
enclosing スコープの例は以下。innerfunc() から、外側の変数 x を参照している
>>> def func(): ... x = 10 ... def innerfunc(): ... print x ... innerfunc() ... >>> >>> func() 10
オブジェクトの属性名に関するルールは、上記とは別である。
タプルがあるおかげで、複数の値を戻す関数を作るのが簡単。実際にはタプルを戻していて、代入時にアンパックが行われているというからくり。
>>> def swap(x, y): ... return y, x ... >>> swap(1, 2) (2, 1) >>> a, b = swap(1, 2) >>> a 2 >>> b 1
引数のバリエーションとして「キーワード引数」「可変長引数」「引数デフォルト値指定」がある。
# キーワード引数の例 >>> def func(a, b, c): print a, b, c ... >>> func(1, 2, 3) 1 2 3 >>> func(c=1, b=3, a=2) 2 3 1 # 引数デフォルト値指定 >>> def func(a, b=0, c=0): print a, b, c ... >>> func(1) 1 0 0 >>> func(1, 2) 1 2 0 # 可変長引数(1) # *args に渡した可変個の引数はタプル化される >>> def times(*args): ... return reduce(lambda x,y:x*y, args) ... >>> times(1,2,3,4,5,6) 720 # 可変長引数(2) # **args に渡した可変個の引数はディクショナリ化される >>> def foo(**args): ... print args ... >>> foo(1,2,3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes exactly 0 arguments (3 given) >>> >>> foo(a=1, b=2) {'a': 1, 'b': 2} >>> >>> foo(a=1, b=2, c=2) {'a': 1, 'c': 2, 'b': 2} >>> >>> foo(a=1, b=2, c=2, a=3) # 同じキーワードがあると後のほうに出てくる値で上書きされるっぽい {'a': 3, 'c': 2, 'b': 2} >>> >>> foo(a=1, b=2, c=2, a=3, c=1, c=0) {'a': 3, 'c': 0, 'b': 2}
Pythonでは、関数は一般に「常に正しい引数が指定される」という前提で作った方が得策。引数が正しくなければ自動的に例外が発生するので、それに任せるが良し。
引数指定に関するNGの例。
# キーワード引数の後に通常の引数を指定してはダメ。 >>> func(a=1, 2) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg # 可変長引数の後に通常の引数は書けない。 >>> def func(*args, b): File "<stdin>", line 1 def func(*args, b): ^ SyntaxError: invalid syntax # *引数は同時に2つは書けない。 >>> def func(*a, *b): File "<stdin>", line 1 def func(*a, *b): ^ SyntaxError: invalid syntax # * と ** の併用はOK。 >>> def func(*a, **b): ... print a, b ... # ただし順序は *, ** の順で。 >>> def func(**a, *b): File "<stdin>", line 1 def func(**a, *b): ^ SyntaxError: invalid syntax
- 14章 関数に関連する高度なテクニック
- lambda 式
lambda は無名関数とか呼ばれる。名前がないので、何かに代入して使う。インラインでの関数定義(コールバック関数とか)に良く使われる。書式は以下の通り:
lambda arg1, arg2, ..., argN : 引数を使用する式
lambda は「式」である。これ重要。def はステートメントなので書ける場所が制限されるが、lambda は式なので、書ける場所が多い。
lambda の例:
# 前出の関数。 >>> def times(*args): ... return reduce(lambda x,y: x*y, args) ... >>> times(1,2,3,4,5,6) 720 # 上記を lambda で書いてみる。 >>> fn = lambda *args: reduce(lambda x,y: x*y, args) >>> fn(1,2,3,4,5,6) 720 # 一応、可変長引数も取れるみたい。キーワード引数や引数デフォルト値も普通に使えそうだな。 >>> fn = lambda a, b=0, c=0: a+b+c >>> fn(1) 1 >>> fn(a=10, b=5) 15 # lambda は式なので、いろんなところで書ける。 >>> L = [(lambda x: x), (lambda x: x**2), (lambda x: x**3), (lambda x: x**4)] >>> >>> for fn in L: ... print fn(3), ... 3 9 27 81 # lambda 内から、外側の変数を覗くことが可能 >>> def func(x): ... return (lambda y: x+y) ... >>> fn = func(10) >>> fn <function <lambda> at 0x00AF5CB0> >>> fn(20) 30 # lambda のネストでも同様に、内側から外側の変数が見える >>> fn = lambda x: (lambda y: x*y) >>> fn <function <lambda> at 0x00AF5DB0> >>> fn(10) <function <lambda> at 0x00AF5CB0> >>> fn(10)(10) 100
14章はこのあとにもリスト内包、ジェネレータなどてんこ盛りな内容なので、とりあえずここまでで一旦ストップ。