ホームページ >バックエンド開発 >Python チュートリアル >Python のクロージャとデコレータを理解する

Python のクロージャとデコレータを理解する

高洛峰
高洛峰オリジナル
2017-03-03 13:58:011214ブラウズ

Python のクロージャは次のように表現的に定義 (解釈) されます: 内部関数内で、外部スコープ (ただしグローバル スコープではない) の変数が参照される場合、内部関数はクロージャ (クロージャ) とみなされます。

以下の手順は主に python2.7 用であり、他のバージョンでは異なる場合があります。

定義を直接見るとよくわからないかもしれませんが、まず内部関数が何であるかを見てみましょう:

def wai_hanshu(canshu_1):

  def nei_hanshu(canshu_2): # 我在函数内部有定义了一个函数
    return canshu_1*canshu_2

  return nei_hanshu  # 我将内部函数返回出去

a = wai_hanshu(123)   # 此时 canshu_1 = 123
print a
print a(321)  # canshu_2 = 321

Python のクロージャとデコレータを理解する

外層 関数が変数を渡して a に代入すると、 a が関数オブジェクトになり、この関数オブジェクトに再度パラメータを渡すと、内部関数の戻り値が得られます。スコープの原則に従って、グローバル スコープ内のローカル スコープにアクセスできないことはわかっています。ただし、ここでは内部関数にトリッキーな方法でアクセスします。 。

例を見てみましょう:

def wai_hanshu():
  a = []
  def nei_hanshu(canshu):
    a.append(canshu)
    return a

  return nei_hanshu

a = wai_hanshu()
print a(123)
print a(321)

Python のクロージャとデコレータを理解する

外部関数にある関数のリスト a が変更されていることがわかります。その理由を知るには、まず Python 名前空間とは何か、そして名前空間がスコープのパフォーマンスの理由であることを知る必要があります。ここで簡単に説明します。

名前空間を導入する主な理由は、変数の競合を避けるためです。Python には多数のモジュールがあり、モジュール内には関数やクラスなどがあり、それらはすべて変数を使用するためです。しかし、他の変数名と競合しないように毎回注意しなければならないのは面倒なので、開発者は他人が書いたプログラムでどのような変数が使用されているかを考えるよりも、自分自身の問題に集中する必要があるため、Python では名前空間が導入されました。名前空間はモジュール層に分かれており、モジュールはグローバルスコープとローカルスコープに分かれています。 図で表すと

Python のクロージャとデコレータを理解する

名前空間はモジュールごとに異なり、グローバルスコープとローカルスコープがあります。変数名が競合しないように、前にネストすることもできます。ちなみに、名前空間の名前は __name__ 属性を通じて取得できることを付け加えておきます:

Python のクロージャとデコレータを理解する

メインファイルの名前空間は '__main__' と呼ばれ、モジュールの名前空間はモジュール名です。

スコープの誕生は、Python が変数を探すときに、まず現在の名前空間でそれを探し、現在の名前空間で見つからない場合は、上位レベルの名前空間でそれを探すためです。同様に、最後に何も見つからなかった場合は、変数が見つからない例外がトリガーされます。

以前から常に言ってきましたが、グローバル スコープはローカル スコープにアクセスできませんが、この理由によりローカル スコープはグローバル スコープにアクセスできます。そして、外部と同じ名前でローカルスコープに変数を作成すると、Python がこの変数を探すとき、まず現在のスコープでそれを探します。見つかった場合は、それを探し続けません。一つ上のレベル。

初期のPythonバージョンでは、ローカルスコープは他のローカルスコープにアクセスできず、グローバルスコープにしかアクセスできませんでしたが、現在のバージョンでは1つ上のレベルの順番で検索されるようになっていますので、ここで触れておきます。

この機能により、内部関数で外部関数の変数にアクセスできます。これはクロージャーとも呼ばれます。

注: ここではオブジェクトを区別する必要があります。例:

def wai_hanshu():
  a = []
  def nei_hanshu(canshu):
    a.append(canshu)
    return a

  return nei_hanshu

a = wai_hanshu()  # 我创建了一个对象
b = wai_hanshu()  # 我又创建了一个对象
print a
print b
print a(123)
print b(321)

Python のクロージャとデコレータを理解する

ここでは、すべて wai_hanshu で変数を操作していますが、a と b は完全に 2 つのオブジェクトです。メモリ空間も同様です。異なるため、内部のデータも独立しています。混同しないように注意してください。

デコレータ

実際、デコレータはクロージャに基づいてさらにいくつかの手順を実行します。コードを見てください:

def zsq(func): # 装饰函数
  def nei():
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei

def login():  # 被装饰函数
  print '我进行了登录功能'

login = zsq(login)  # 我将被装饰的函数传入装饰函数中,并覆盖了原函数的入口

login()   # 此时执行的就是被装饰后的函数了

Python のクロージャとデコレータを理解する

在看这段代码的时候,要知道几件事:

1.函数的参数传递的其实是引用,而不是值。

2.函数名也是一个变量,所以可以重新赋值。

3.赋值操作的时候,先执行等号右边的。

只有明白了上面这些事之后,再结合一下代码,应该就能明白什么是装饰器了。所谓装饰器就是在闭包的基础上传递了一个函数,然后覆盖原来函数的执行入口,以后调用这个函数的时候,就可以额外实现一些功能了。装饰器的存在主要是为了不修改原函数的代码,也不修改其他调用这个函数的代码,就能实现功能的拓展。

而python觉得让你每次都进行重命名操作实在太不方便,于是就给出了一个便利的写法:

def zsq(func):
  def nei():
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei

@zsq  # 自动将其下面的函数作为参数传到装饰函数中去
def login():
  print '我进行了登录功能'


login()

Python のクロージャとデコレータを理解する

这些小便利也叫做python的语法糖,你可能在很多地方见过这个说法。

带参数的装饰器:

def zsq(a):
  print '我是装饰器的参数', a
  def nei(func):
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei


@zsq('123')
def login():
  print '我进行了登录功能'

Python のクロージャとデコレータを理解する

相当于: login = zsq(123)(login) ,所以在这里没有调用就执行了。

装饰器的嵌套:

这里就不完整写个例子了:

@deco1(deco_arg) 
@deco2 
def func(): 
  pass

相当于: func = deco1(deco_arg)(deco2(func)) 

也就是从上到下的嵌套了。

关于闭包和装饰器就先讲到这里,以后有需要再补充。

以上这篇深入Python のクロージャとデコレータを理解する就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。

更多Python のクロージャとデコレータを理解する相关文章请关注PHP中文网!

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