Pythonのいくつかの高度な文法概念を簡単に分析します。


1.匿名関数
匿名関数(anonymous function)は、任意の識別子と紐付けられていない関数を指し、functional programming laggage領域で多く使用され、典型的なアプリケーションの場合:
1)パラメータとして高次関数(higher-order function)に伝達され、例えばpythonのbuilt-in関数filter/map/reduceは典型的な高次関数である。
2)高次関数の戻り値として(ここの「値」は実際には関数オブジェクトですが)
ネーミング関数(named function)と比較して、関数が1回または限られた回だけ呼び出された場合、匿名関数は文法的にはより軽量である。
具体的な構文では、pythonは、lamda構文サポート関数を通じて、表現式の匿名関数として使用されます。すなわち、pythonのlamda表現は本質的には匿名関数ですが、その関数は式だけであり、他の語句を含んではいけません。
さらに、高レベルの動的言語は、匿名関数によって、クローズドまたはデコレーションなどの高度な構文を達成することが多い。
いくつかの場合、lamda表現の使用はpythonプログラムを非常に簡潔に見えるようにする。例えば、以下はvalueに従ってdict要素を並べ替えるコードの例である。

>>> foo = {'father' : 65, 'mother' : 62, 'sister' : 38, 'brother' : 29, 'me' : 28}
>>> sorted(foo.iteritems(), key=lambda x: x[1])
[('me', 28), ('brother', 29), ('sister', 38), ('mother', 62), ('father', 65)]
2.クローズド
クローズド(closure)は、本質的には、その参照環境の関数または関数参照を含むものであり、ここでの「引用環境」は通常、関数的な体験訪問の非局所変数(non-local variables)の参照を格納するテーブルによって維持される。
C言語の関数ポインタと比較して、クローズドはネスト関数がその作用域外のnon-local変数にアクセスすることを許可します。これはPythonインタプリタの変数に対する作用領域検索規則に関連しています。この文章を見たりして、素早く理解します)。
実行時のメモリ割り当てモデルは、線形スタック上で局所変数を作成する言語(典型的にはC言語のような)であり、通常はクローズドをサポートするのが難しい。これらの言語の下の実装では、関数が戻ると、関数で定義された局所変数は、関数スタックの回収によって破壊されるからです。しかし、下の実装において、そのアクセスを要求するnon-local変数は、クローズドが実行されている間は有効に維持され、このクローズドのライフサイクルが終了するまでは、これらのnon-local変数は、予期しないことに、これらの変数を定義する関数とともに廃棄されることはできません。したがって、生まれつきのクローズドサポート言語は、通常はgarbage collection方式でメモリを管理しています。gcメカニズムは変数が参照されない時だけ、システムによって破壊され、そのメモリ空間を回収することを保証しています。
具体的な文法では、クローズドは通常関数ネスト定義を伴う。Pythonを例にとって、簡単なクローズドの例は以下の通りである。

#!/bin/env python
#-*- encoding: utf-8 -*-

def startAt_v1(x):
 def incrementBy(y):
  return x + y 
 print 'id(incrementBy)=%s' % (id(incrementBy))
 return incrementBy

def startAt_v2(x):
 return lambda y: x + y 

if '__main__' == __name__:
 c1 = startAt_v1(2)
 print 'type(c1)=%s, c1(3)=%s' % (type(c1), c1(3))
 print 'id(c1)=%s' % (id(c1))
 
 c2 = startAt_v2(2)
 print 'type(c2)=%s, c2(3)=%s' % (type(c2), c2(3))
実行結果は以下の通りです。

id(incrementBy)=139730510519782
type(c1)=<type 'function'>, c1(3)=5
id(c1)=139730510519782
type(c2)=<type 'function'>, c2(3)=5
上記の例では、startAt_v 1とstartAt_v 2は、ともに、ネスト定義関数を介してv 1が実装されている。v 2は、lamda式/匿名関数により実現される。
私たちはv 1を例にとって、クローズドについて説明します。
1)関数startAt_v 1は1つのパラメータを受け取り、1つの関数オブジェクトを返します。この関数オブジェクトの挙動はネスト定義の関数increment Byによって実現されます。
2)関数increment Byにとって、変数xはいわゆるnon-local変数である(xはこの関数で定義された局所変数でもないし、一般的な意味での大域変数でもないので)、increment Byは具体的な関数的な挙動を実現して戻ってきます。
3)main入口のc 1で受信した戻り値は関数オブジェクトであり、id(increment By)==id(c 1)から判断すると、c 1の「指向」のオブジェクトと関数名increment Byの「指向」は同じ関数オブジェクトである。
4)Pythonがクローズドに対してサポートされていることから、C 1が指すオブジェクトは、通常の関数の対象と比較して、その関数のスコープ内にないnon-local変数にアクセスできますが、この変数はincrement Byの外殻包装関数startAt_によって指定されています。v 1のパラメータが提供されるので、c 1が指す関数オブジェクトに相当する外層包装関数の入力には「記憶」機能があり、外層包装関数を呼び出してクローズドを作成すると、異なるパラメータが内層関数として参照環境として維持される。
5)c 1(3)を呼び出したとき、入ってきたパラメータは環境保護の外層包装関数のパラメータと一緒に演算され、最終結果が得られます。
以上の手順では、作成から実行までのクローズドパッケージの基本原理を説明しました。このcaseを理解したら、クローズドの概念も明らかになるはずです。
3.飾り器
pythonは装飾器文法をサポートします。装飾器の概念は初心者にとっては分かりにくいです。これは関数式プログラミングのいくつかの概念(匿名関数、クローズドなど)に関連しています。これも匿名関数とクローズドの原因を先に紹介します。
私たちはこの記事を引用して装飾器の定義を説明します。
A decorator is a function that tars a function oject as an argment、and returns a function object as a return value.
この定義から分かるように、装飾器は本質的にただの関数であり、その関数(装飾関数と呼ばれる)の挙動を、クローズドの文法によって修正します。すなわちデコラレータは実はクローズド関数であり、この関数は装飾関数名(この関数名は関数オブジェクトの参照です。)を参照して、クローズド内で装飾関数の挙動を修正した後、新しい関数オブジェクトを返します。
特に説明します。decoratorは関数として現れなくてはいけません。例えば、classとしてもいいです。この文章の例を参照してください。
関数装飾器を定義した上で、これを装飾関数として外部から呼び出した場合、デコンタのシンタックスキャンディーは、Python解釈器によって、まず装飾関数を実行したと解釈され、その後、装飾器が返した新しい関数オブジェクト上で残りのステートメントを実行し続ける。
実例を分析してみます。

#!/bin/env python
#-*- encoding: utf-8 -*-

def wrapper(fn):
 def inner(n, m):
  n += 1
  print 'in inner: fn=%s, n=%s, m=%s' % (fn.__name__, n, m)
  return fn(n, m) + 6 //    return     int  
 return inner

@wrapper
def foo(n, m):
 print 'in foo: n=%s, m=%s' % (n, m)
 return n * m

print foo(2, 3)
上記の例では、fooは@wrapper文法糖によって宣言されていますが、wrapperでは入れ子のinner関数が定義されています(この関数のパラメータリストは装飾関数fooのパラメータリストと一致していなければなりません)。飾りwrapperはfooの挙動を修正して、innerに戻ります。だからwrpperが最終的に戻ったのもint対象です。
foo(2,3)を呼び出した場合、Python解釈器はまずwrapperを呼び出してfooを書き換え、intオブジェクトに戻します。上記のコードの実行結果は以下の通りです。

in inner: fn=foo, n=3, m=3
in foo: n=3, m=3
foo(2, 3)=15