ホームページ  >  記事  >  バックエンド開発  >  Python のいくつかの高度な構文概念の簡単な分析 (ラムダ式クロージャ デコレータ)

Python のいくつかの高度な構文概念の簡単な分析 (ラムダ式クロージャ デコレータ)

高洛峰
高洛峰オリジナル
2017-01-23 14:38:461430ブラウズ

1. 匿名関数
匿名関数は、関数型プログラミング言語の分野で主に使用されます。
1) 高階関数にパラメーターとして渡されます。高階関数) )、Python の組み込み関数 filter/map/reduce などはすべて典型的な高階関数です
2) 高階関数の戻り値として (ただし、ここでの「値」は実際には関数オブジェクト)
名前付き関数を使用する (名前付き関数) と比較して、関数が 1 回だけ呼び出されるか限られた回数だけ呼び出される場合、匿名関数は構文的に軽量です。
特定の構文に関して、Python は関数本体がラムダ構文による式である匿名関数をサポートしています。つまり、Python のラムダ式は本質的に匿名関数ですが、その関数本体には式のみを指定でき、他のステートメントを含めることはできません。
さらに、高水準動的言語は、クロージャやデコレータなどの高度な構文を実装するために匿名関数を使用することがよくあります。
場合によっては、ラムダ式を使用すると、Python プログラムが非常に簡潔に見えます。たとえば、次は値に従って 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. クロージャ
クロージャは本質的に、その参照環境 (参照環境) を含む関数または関数参照です。ここでは、参照環境は通常、関数本体がアクセスする非ローカル変数への参照を格納するテーブルによって維持されます。
C 言語の関数ポインターと比較すると、クロージャーを使用すると、入れ子になった関数がスコープ外の非ローカル変数にアクセスできるようになります。これは、Python インタープリターの変数のスコープ検索ルールに関連しています (Python は LEGB の検索ルールをサポートしています。そうであれば、さらに詳しく調べたいと思います)。 『Python を学ぶ』第 4 版の第 17 章にあるスコープと検索ルールの詳細な説明を参照するか、この記事を確認して簡単に理解することができます)。
ランタイムメモリ割り当てモデルが線形スタック上にローカル変数を作成する言語 (通常は C 言語) では、通常、クロージャをサポートすることが困難です。これらの言語の基礎となる実装では、関数が返されると、関数スタックがリサイクルされるときに関数内で定義されたローカル変数が破棄されるためです。ただし、クロージャの基礎となる実装では、クロージャが実行されるとき、クロージャのライフサイクルが終了するまで、アクセスしたい非ローカル変数が有効なままであることが必要です。これは、これらの非ローカル変数は、それらが決定された場合にのみアクセスできることを意味します。これらの変数は、使用されたときにのみ破棄でき、これらの変数を定義する関数が返されたときには破棄できません。したがって、クロージャを自然にサポートする言語は、通常、ガベージ コレクションを使用してメモリを管理します。これは、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 &#39;function&#39;>, c1(3)=5
id(c1)=139730510519782
type(c2)=<type &#39;function&#39;>, c2(3)=5

上記の例では、startAt_v1 と startAt_v2 の両方がクロージャを実装しています。そのうちの 1 つは、v1 がネストされたものを使用して実装されているということです。定義関数; v2 はラムダ式/匿名関数を使用して実装されます。
クロージャを説明するために v1 を例に挙げます:
1) 関数 startAt_v1 は 1 つのパラメーターを受け取り、関数オブジェクトを返します。この関数オブジェクトの動作は、ネストされた定義済み関数 incrementBy によって実装されます。
2) 関数 incrementBy の場合、変数 x はいわゆる非ローカル変数です (x は関数によって定義されたローカル変数でも、通常の意味でのグローバル変数でもないため)。
3) メイン入口の c1 が受け取る戻り値は関数オブジェクトです。 id(incrementBy) == id(c1) より、オブジェクト c1 が「を指している」、関数名 incrementBy が「を指している」と結論付けることができます。 " は実際には同じ関数オブジェクトです。
4) Python のクロージャーサポートの恩恵を受けて、通常の関数のオブジェクトと比較して、c1 が指すオブジェクトはその関数のスコープ内にない非ローカル変数にアクセスでき、この変数は外部ラッパー関数 startAt_v1 によって制御されます。 incrementBy の入力パラメータが提供されるため、c1 が指す関数オブジェクトには、外部ラッピング関数の入力パラメータ用の「メモリ」関数があります。外部ラッピング関数を呼び出してクロージャを作成すると、さまざまな入力パラメータが参照されます。内部機能によって環境を維持します。
5) c1(3)を呼び出すと、渡されたパラメータは、環境メンテナンスを参照する外部パッケージ関数のパラメータとともに計算され、最終結果が得られます。
上記のステップ分析は、作成から実行までのクロージャの基本原理を示しています。このケースを理解すると、クロージャの概念も明確になるはずです。

3. デコレーター
Python はデコレーター構文をサポートします。デコレータの概念は、関数型プログラミングのいくつかの概念 (匿名関数やクロージャなど) に関係しているため、初心者にとっては比較的わかりにくいです。このため、この記事では最初に匿名関数とクロージャを紹介します。

我们引用这篇文章对装饰器的定义:
A decorator is a function that takes a function object as an argument, and returns a function object as a return value.
从这个定义可知,装饰器本质上只是一个函数,它借助闭包的语法去修改一个函数(又称被装饰函数)的行为,即decorator其实是个闭包函数,该函数以被装饰函数名(这个函数名其实是一个函数对象的引用)作为入参,在闭包内修改被装饰函数的行为后,返回一个新的函数对象。
特别说明:decorator并非必须以函数形式出现,它可以是任何可被调用的对象,例如它也可以class形式出现,参见这篇文章给出的例子。
在定义好函数装饰器的前提下,当外部调用这个被装饰函数时,decorator的语法糖会由Python解释器解释为先执行装饰器函数,然后在装饰器返回的新函数对象上继续执行其余语句。
来个实例分析一下:

#!/bin/env python
#-*- encoding: utf-8 -*-
 
def wrapper(fn):
 def inner(n, m):
  n += 1
  print &#39;in inner: fn=%s, n=%s, m=%s&#39; % (fn.__name__, n, m)
  return fn(n, m) + 6 // 这里有return且返回值为int对象
 return inner
 
@wrapper
def foo(n, m):
 print &#39;in foo: n=%s, m=%s&#39; % (n, m)
 return n * m
 
print foo(2, 3)

上面的示例中,foo通过@wrapper语法糖声明它的装饰器是wrapper,在wrapper中,定义了嵌套的inner函数(该函数的参数列表必须与被装饰函数foo的参数列表保持一致),装饰器wrapper修改foo的行为后,返回inner(注意:由于inner的返回值是个int对象,故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

更多Python的几个高级语法概念浅析(lambda表达式闭包装饰器)相关文章请关注PHP中文网!

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。