クローズド
10702 ワード
Pythonを学ぶ時、自分がJavaScriptの基礎があることを喜んで、学習の過程の中で、多くの類似の地方を発見して、例えばパケットを導く方式、パラメータの解包、lambdaの表現式、閉包、引用の賦値、関数はパラメータとしてなどです.装飾器はPython言語の一つのポイントであり、装飾器の使用を学ぶには、まず閉包から見て、装飾器の基本原理を把握しなければならない.
ワードスコープ
閉パケットは関数がその存在する文法的役割ドメインにアクセスして記憶できるものであり,PythonもJavaScriptも文法的役割ドメインに基づいているため,両者の閉パケットの概念は共通している.Pythonが動的役割ドメインではなく文法的役割ドメインであることを説明するために、次の例を見ることができます.
実行結果:
エラーが発生しました.
実行結果:
ここで
クローズドパッケージ
前述したように,閉パケットは関数がその存在する文法的役割ドメインにアクセスし記憶できる特性である.では、関数がこの特性を持っている限り、この関数は閉パケットであり、理論的には、すべての関数は閉パケットであり、彼らが存在する文法の役割ドメインにアクセスできるためです.このような関数も閉パッケージです.
理論的にはそうですが、実際には、閉パケットの定義は少し複雑ですが、前の理論に基づいています.1つの関数(外層関数)で別の関数(内層関数)を返すと、内層関数が外層関数が存在する役割ドメインにアクセスできるので、閉パケットと呼ばれます.次に例を示します.
実行結果は次のとおりです.
上記のように、
閉パッケージでパラメータ関数を呼び出す
JavaScriptと同様に、Pythonでは関数をパラメータとして渡すこともできます.Pythonは参照伝達値であるため、実際に伝達されるパラメータは元の関数自体ではなく、元の関数の参照です.閉パッケージの特性に基づいて、閉パッケージでこの関数にアクセス(または呼び出す)することもできます.
実行結果:
アクセサリー導入
閉包を知ったら、装飾器を話してもいいです.このような需要があることを想像してみてください.あなたの会社にはいくつかの核心的な底辺の方法があります.その後、会社は徐々に大きくなり、他の部門を増やしました.これらの部門には自分の業務がありますが、これらの核心的な底辺の方法を使用しています.あなたの部門もそうです.ある日、プロジェクトマネージャがあなたを見つけて、コアコードに検証などを加えるようにしましたが、コアコードを修正することはできません(そうしないと他の部門に影響します).どうすればいいですか?まず、この方法を考えるかもしれません.
一時関数は、関数をパラメータとして外層関数 に伝達する限り、任意の関数をパッケージすることができる.は、外層関数を実行するときに、戻り値に任意の名前を付けることができる .
あなたが書いたコードはこうです.
大成功!完璧です.同時におめでとうございます.あなたはすでに装飾器を実現しました.装飾器の核心原理は:閉包+関数実参です.
Pythonオリジナルアクセサリーサポート
上では簡単な装飾器を実現し、装飾器の基本原理も知っています.実は、Python言語では、装飾器に対するオリジナルのサポートがありますが、核心原理は依然として変わらず、私たちの操作を簡略化しています.
実行結果は次のとおりです.
Pythonオリジナルのアクセラレータサポートでは、パラメータの変更や変更の手順を省き、アクセラレータを適用すると、アクセラレータの下の関数(ここでは
アクセラレータ関数の実行タイミング
アクセサリー関数(つまり、前に述べた外層関数)はいつ実行されますか?簡単な検証が可能です.
実行結果:
ここでは
たじゅうかざり
関数に複数のアクセサリを適用することもできます.
実行結果は次のとおりです.
出力効果から、デコライザの実行は下から上へ、下位デコライザの実行が完了した後に関数を返して上位のデコライザに渡されることがわかります.
被装飾関数にパラメータを渡す
被装飾関数にパラメータを渡す必要がある場合は、被装飾関数が返す
実行結果:
ここで、パラメータ解包の問題について説明する.
戻り値のある関数をパッケージ化
被包装関数に戻り値がある場合、どのようにして包装で戻り値を取得しますか?まず次の例を見てみましょう.
実行結果:
なぜ関数実行の戻り値は
実行結果は次のとおりです.
アクセサリーのパラメータ
場合によっては、関数を異なる状況で装飾したい場合があります.次の2つの処理方法があります.は、複数の異なる条件における装飾器を定義し、条件に応じて異なる装飾器 を適用する.は、条件によって装飾 を装飾器内部で行う装飾器を定義する.
1つ目の方法は簡単ですが、ここでは2つ目の方法について説明します.装飾器の内部で異なる条件を判断するには、パラメータを入力する1つ以上のパラメータが必要です.
実行結果は次のとおりです.
類を装飾器とする
関数に加えて、クラスは装飾器としてもよいが、クラスを装飾器として言う前に、まず
__call__ 方法
作成したインスタンスも呼び出すことができます.インスタンスオブジェクトを呼び出すと、その内部の
実行結果:
類を装飾器とする
オブジェクトの
実行結果:
元の装飾関数を保存
装飾後の
実行結果は次のとおりです.
まとめ
本文は主にPythonの中の閉包と装飾器の概念を述べて、主に以下の内容があります: Pythonは文法に基づくドメイン である.閉パケットは、関数が記憶してアクセスできる文法的役割ドメイン である.ギフトバッグによるシンプルなデコレーション を実現 Pythonオリジナルアクセサリー対応 関数に複数の装飾器 を適用する被装飾関数に をどのように伝達するか戻り値のある関数にデコレーション を適用する方法異なる条件に従って関数に異なる装飾器 を適用する方法類を装飾器とする場合及び .
終わります.
ワードスコープ
閉パケットは関数がその存在する文法的役割ドメインにアクセスして記憶できるものであり,PythonもJavaScriptも文法的役割ドメインに基づいているため,両者の閉パケットの概念は共通している.Pythonが動的役割ドメインではなく文法的役割ドメインであることを説明するために、次の例を見ることができます.
def getA():
return a
def test():
a = 100
print(getA())
test()
実行結果:
Traceback (most recent call last):
File "C:\Users\Charley\Desktop\py\py.py", line 8, in
test()
File "C:\Users\Charley\Desktop\py\py.py", line 6, in test
print(getA())
File "C:\Users\Charley\Desktop\py\py.py", line 2, in getA
return a
NameError: name 'a' is not defined
エラーが発生しました.
getA
関数が存在する役割ドメインで変数a
を宣言します.def getA():
return a
a = 10010
def test():
a = 100
print(getA())
test()
実行結果:
10010
ここで
10010
が出力され、getA
関数が文法的役割ドメインに依存していることを示し、その役割ドメインは関数定義の初めから決定され、その役割ドメインは呼び出し位置の影響を受けない.文法的役割ドメインを理解し,閉パッケージを理解した.クローズドパッケージ
前述したように,閉パケットは関数がその存在する文法的役割ドメインにアクセスし記憶できる特性である.では、関数がこの特性を持っている限り、この関数は閉パケットであり、理論的には、すべての関数は閉パケットであり、彼らが存在する文法の役割ドメインにアクセスできるためです.このような関数も閉パッケージです.
a = 100
def iAmClosure():
print(a)
iAmClosure()
理論的にはそうですが、実際には、閉パケットの定義は少し複雑ですが、前の理論に基づいています.1つの関数(外層関数)で別の関数(内層関数)を返すと、内層関数が外層関数が存在する役割ドメインにアクセスできるので、閉パケットと呼ばれます.次に例を示します.
def outer():
a = 100
def inner():
print(a)
return inner
outer()()
実行結果は次のとおりです.
100
上記のように、
inner
関数は閉パケットです.閉パッケージでパラメータ関数を呼び出す
JavaScriptと同様に、Pythonでは関数をパラメータとして渡すこともできます.Pythonは参照伝達値であるため、実際に伝達されるパラメータは元の関数自体ではなく、元の関数の参照です.閉パッケージの特性に基づいて、閉パッケージでこの関数にアクセス(または呼び出す)することもできます.
def outer(fn):
def inner():
#
# fn
fn()
return inner
def test():
print("We will not use 'Hello World'")
ret = outer(test)
ret()
実行結果:
We will not use 'Hello World'
アクセサリー導入
閉包を知ったら、装飾器を話してもいいです.このような需要があることを想像してみてください.あなたの会社にはいくつかの核心的な底辺の方法があります.その後、会社は徐々に大きくなり、他の部門を増やしました.これらの部門には自分の業務がありますが、これらの核心的な底辺の方法を使用しています.あなたの部門もそうです.ある日、プロジェクトマネージャがあなたを見つけて、コアコードに検証などを加えるようにしましたが、コアコードを修正することはできません(そうしないと他の部門に影響します).どうすればいいですか?まず、この方法を考えるかもしれません.
def core():
pass
def fixCore():
doSometing()...
core()
fixCore()
core
関数を1つの外層関数でパッケージし、検証機能が実行された後、core
関数を呼び出す.この時、プロジェクトマネージャはまた、私たちの呼び出し方法を変えることはできません.私たちはcore
で呼び出したいと思っています.そこで、コードを変更しました.def core():
pass
tmp = core;
def fixCore():
tmp()
core = fixCore
core()
一時関数
tmp
でcore
とfixCore
を交換し、タヌキは太子を交換した.これでcore
を楽しくそのまま使えます.これはプロジェクトマネージャがまた言ったことです.私たちは複数のコア関数を包装する必要があります.変数をすべて交換することはできません.そして、これは優雅ではありません.他の方法を考えてみましょう.はい、要求が多いですね.そこで考えてみると、パッケージを閉じる方法を考えました.パッケージする必要がある関数をパラメータとして外層関数に入力し、外層関数はいくつかの検証操作を実行した後、パッケージ関数を呼び出す内層関数を返します.このようなメリットは、あなたが書いたコードはこうです.
# ,
def outer(fn):
def inner():
doSometing()...
fn()
return inner
# 1
def core1():
pass
# 2
def core2():
pass
# core1
core1 = outer(core1)
# core2
core2 = outer(core2)
#
core1()
core2()
大成功!完璧です.同時におめでとうございます.あなたはすでに装飾器を実現しました.装飾器の核心原理は:閉包+関数実参です.
Pythonオリジナルアクセサリーサポート
上では簡単な装飾器を実現し、装飾器の基本原理も知っています.実は、Python言語では、装飾器に対するオリジナルのサポートがありますが、核心原理は依然として変わらず、私たちの操作を簡略化しています.
# ,
def outer(fn):
def inner():
print("---- ----")
fn()
return inner
#
@outer
# 1
def core1():
print("----core1----")
#
@outer
# 2
def core2():
print("----core2----")
core1()
core2()
実行結果は次のとおりです.
---- ----
----core1----
---- ----
----core2----
Pythonオリジナルのアクセラレータサポートでは、パラメータの変更や変更の手順を省き、アクセラレータを適用すると、アクセラレータの下の関数(ここでは
core1
とcore2
)をパラメータとして、元の関数を上書きする新しい関数を生成します.アクセラレータ関数の実行タイミング
アクセサリー関数(つまり、前に述べた外層関数)はいつ実行されますか?簡単な検証が可能です.
def outer(fn):
print("---- ----")
def inner():
print("---- ----")
fn()
return inner
@outer
def core1():
print("----core1----")
@outer
def core2():
print("----core2----")
実行結果:
---- ----
---- ----
ここでは
core1
とcore2
の関数を直接呼び出しておらず,装飾器関数が実行された.すなわち,解釈器実行中に装飾器に遭遇すると,装飾器関数が実行される.たじゅうかざり
関数に複数のアクセサリを適用することもできます.
def outer1(fn):
def inner():
print("----outer1 ----")
fn()
return inner
def outer2(fn):
def inner():
print("----outer2 ----")
fn()
return inner
@outer2
@outer1
def core1():
print("----core1----")
core1()
実行結果は次のとおりです.
----outer2 ----
----outer1 ----
----core1----
出力効果から、デコライザの実行は下から上へ、下位デコライザの実行が完了した後に関数を返して上位のデコライザに渡されることがわかります.
被装飾関数にパラメータを渡す
被装飾関数にパラメータを渡す必要がある場合は、被装飾関数が返す
inner
関数に文章を書き、そのエージェントに被装飾関数のパラメータを受け入れさせ、被装飾関数に渡す必要があります.def outer(fn):
def inner(*args,**kwargs):
print("----outer ----")
fn(*args,**kwargs)
return inner
@outer
def core(*args,a,b):
print("----core1----")
print(a,b)
core(a = 1,b = 2)
実行結果:
----outer ----
----core1----
1 2
ここで、パラメータ解包の問題について説明する.
inner
の関数の*
および**
は、その関数が受け入れる可変パラメータおよびキーワードパラメータを表し、パラメータ関数fn
を呼び出すときに*
および**
は、JavaScriptの拡張演算子...
と同様に、可変パラメータおよびキーワードパラメータを解包することを表す.args
およびkwargs
を直接パラメータとして被装飾関数に渡す場合、被装飾関数は1つのメタセットおよび辞書のみを受信するので、パケットを解いた後に伝達する必要がある.戻り値のある関数をパッケージ化
被包装関数に戻り値がある場合、どのようにして包装で戻り値を取得しますか?まず次の例を見てみましょう.
def outer(fn):
def inner():
print("----outer ----")
fn()
return inner
@outer
def core():
return "Hello World"
print(core())
実行結果:
----outer ----
None
なぜ関数実行の戻り値は
None
なのでしょうか.Hello World
ではないでしょうか.これは装飾の過程が実は置換の過程を引用するためで、装飾の前で、core
変数はその初期の関数体から指向して、装飾の後で再び指向して、装飾器の関数が返したinner
関数に指向して、私達はinner
関数に定義の戻り値を与えていないで、装飾を呼び出したcore
関数も、自然に戻り値がありません.装飾後の関数に戻り値を残すには、inner
関数を装飾前の関数の戻り値に戻すだけです.def outer(fn):
def inner():
print("----outer ----")
return fn()
return inner
@outer
def core():
return "Hello World"
print(core())
実行結果は次のとおりです.
----outer ----
Hello World
アクセサリーのパラメータ
場合によっては、関数を異なる状況で装飾したい場合があります.次の2つの処理方法があります.
1つ目の方法は簡単ですが、ここでは2つ目の方法について説明します.装飾器の内部で異なる条件を判断するには、パラメータを入力する1つ以上のパラメータが必要です.
# main ,
def main(flag):
# flag True
if flag:
def outer(fn):
def inner():
print(" Flag")
fn()
return inner
return outer
# flag False
else:
def outer(fn):
def inner():
print("Flag !")
fn()
return inner
return outer
# main True
@main(True)
def core1():
pass
# main False
@main(False)
def core2():
pass
core1()
core2()
実行結果は次のとおりです.
Flag
Flag !
main
に異なるパラメータを入力することに基づいて、core1
およびcore2
関数に異なる装飾器を適用した.ここでのmain
関数は装飾関数ではなく,その戻り値こそ装飾関数であり,main
関数の戻り値に基づいてターゲット関数を装飾した.類を装飾器とする
関数に加えて、クラスは装飾器としてもよいが、クラスを装飾器として言う前に、まず
__call__
の方法を理解する必要がある.__call__ 方法
作成したインスタンスも呼び出すことができます.インスタンスオブジェクトを呼び出すと、その内部の
__call__
メソッドが実行されます.このメソッドは手動で実装する必要があります.このメソッドがなければ、インスタンスは呼び出されません.class Test(object):
def __call__(self):
print(" ")
t = Test()
t()
実行結果:
類を装飾器とする
オブジェクトの
__call__
メソッドは、オブジェクトが呼び出されたときに実行されることが知られているが、クラスがデコレーションの結果としてそのオブジェクトにデコレーションされた関数を指し、そのオブジェクトが呼び出されたときにオブジェクトの__call__
メソッドを実行し、デコレーションされた関数に__call__
メソッドを実行させるには、まずオブジェクトが作成され、したがって、__new__
メソッドと__init__
メソッドが連動し、オブジェクトの作成時にtest
関数がパラメータ伝達オブジェクトの__init__
メソッドとして扱われます.class Test(object):
# __new__
def __new__(self,oldFunc):
print("__new__ ")
return object.__new__(self)
# __init__
def __init__(self,oldFunc):
print("__init__ ")
# __call__
def __call__(self):
print(" ")
#
@Test
def test():
print(" test ~~")
test()
実行結果:
__new__
__init__
元の装飾関数を保存
装飾後の
test
関数は新しいオブジェクトを指していますが、装飾される前の元の関数を保存する方法はありますか?前述したように、オブジェクトを新規作成するときに、装飾された関数が__new__
メソッドと__init__
メソッドにパラメータとして渡されるため、この2つのメソッドで元の関数の参照を取得することができます.class Test(object):
# __new__
def __new__(self,oldFunc):
print("__new__ ")
return object.__new__(self)
# __init__
def __init__(self,oldFunc):
print("__init__ ")
self.__oldFunc = oldFunc
# __call__
def __call__(self):
print(" ")
self.__oldFunc()
#
@Test
def test():
print(" test ~~")
test()
実行結果は次のとおりです.
__new__
__init__
test ~~
まとめ
本文は主にPythonの中の閉包と装飾器の概念を述べて、主に以下の内容があります:
__call__
方法終わります.