Python変数作用域LEGB

6317 ワード

レビュー-Decorator
前編では、閉包と装飾器の概念について述べた.閉包とは、関数内部にネストする関数である.装飾器はただ閉包する特殊なシーンで、特殊はもし外の関数のパラメータが1つを指すならば、装飾される関数の住所に用いる時(必ずしも住所ではありませんて、勝手に良いです)、"@xxx"のこのような書き方があって、やはりとても面白いです.装飾器の役割は、元の関数のコードを変更しない前提の下で、元の関数に新しい機能を追加することである.書き方からすると、やはり簡潔で優雅だ.
装飾器の通俗的な書き方
#         
def out(func):
    def inner(*args, **kwargs):
        print("we are checking...", args[0])
        return func(*args, **kwargs)

    return inner


@out
def check_2019_nCov(name):
    return f"now, {name} is very healthy..."


tmp = check_2019_nCov('youge')
print(tmp)

# output
we are checking... youge
now, youge is very healthy...

飾りに参列する
このような「@」の書き方は、外関数のパラメータがfuncアドレスであることを要求するが、パラメータを伝達できるようにするには、外パンに関数を1層(パラメータを受け入れる役割)すれば、作用空間を拡大し、パラメータを得ることになるのではないか.
#          ,         
def get_param(*args, **kwargs):
    def out(func):
        def inner(*args, **kwargs):
            print("get params", args, kwargs)
            return func(*args, **kwargs)

        return inner

    return out


@get_param("youge")
def check_2019_nCov(name):
    return f"now, {name} is very healthy..."



tmp = check_2019_nCov("youge")
print(tmp)

# output
get params ('youge',) {}
now, youge is very healthy...

この種のデザイナがパラメータを伝達するアプリケーションシーンは、Webアプリケーションにおいて、Flaskを例として、すべてのルーティングurlの概念である、route(「/login」)のような書き方は、様々なデザイナでルーティング->ビューのマッピング関係を実現することが原理である.
よく見ると、プロセス全体は重要な話題、すなわちネーミング空間、変数の役割ドメイン、あるいはネーミング空間がどのようなものかを無視している.
LEGB法則
ネーミングスペース
前編で詳しく述べたように、Python変数の本質は指針であり、対象の引用であり、Pythonの中の万物はすべて対象である.このオブジェクトは、真に記憶するデータのメモリアドレスであり、各種類(データ型、データ構造)の例である.(変数は参照対象に用いる)差が少ないという意味でしょう.
最も直感的な解釈:
"A namespace is a mapping from names to objects". (変数名とオブジェクトのマッピング)
"Most namespaces are currently implemented as Python dictionaries."(ほとんどのネーミングスペースは辞書で実現されます)
すなわち、ネーミングスペースは、変数のネーミング競合を回避するための制約である.それぞれのネーミング空間は互いに独立しており、一つの空間では再名することはできず、異なる空間では関係ない.コンピュータシステムと同じ論理でファイルを格納します.
for i in range(10):
    print(i)
    
#          i             .
    
[i for i in range(100)]
  • 内蔵スペース:(built-in names):Python内蔵関数、異常クラスなどのPython内蔵名...
  • グローバルスペース:(global names):定数、モジュールで定義された名前(クラス、インポートモジュール)...
  • Enclosed:関数にネストされている可能性のある関数など...
  • ローカル名:(local names):関数で定義された名前(関数内の変数)...

  • Python検索変数は、Local->Enclosed->Global->Built-inの順です.
    実は、私の個人的な経験から言えば、局部と全局の相対性を区別することができます.基本的には直感的には、コードを書くpyファイルを例に挙げる.最外層には、変数、クラス定義、関数定義、fromから...import .. の変数あるいは関数名、これらはグローバル変数で、最も外のクラスあるいは関数、中はそれぞれの名前の空間です.
    # var1   global
    var1 = 666
    
    def foo():
        # var2    
        var2 = 666
        def foo2():
            #      
            var3 = 666
            
            # print(var2)
            
    print(var3)  # G->L       
    #   foo2      var2   L->E  ok 
    #   foo       var2   E->L     

    実はよく理解しています.上段codeについては、L-E-G-Bの法則により、一つの相対を理解すればよい.
    グローバルvsローカル
    total = 0  #   
    
    def sum(a, b):
        """    sum"""
        total = a + b  
        print("  total:", total)
        
    sum(1, 1)
    print("  total:", total)
    
    # output
      total: 2
      total: 0

    局所はグローバルを変えることはなく、局所内でグローバル変数を得ることができることがわかる.さもないと閉包して、外関数が受信したパラメータ、内関数はどのように手に入れることができますか?外の関数で、“拡充しました”内の関数の作用域、L->E->G->Bの法則によって探し当てます.
    globalとnonlocal
    name = "youge"
    
    def change_name():
        name = "youyou"
        
    
    #     "youge"    "youyou"
    change_name()
    print(name)
    
    # output
    youge

    改めることができなかったことに気づいたのは自然だ.なぜなら、関数を呼び出すとき、中のnameはLocal変数であり、グローバルなnameには影響しないので、関数の内部でグローバル変数を変更したい場合は、その変数をglobalキーワードで宣言すればよい.
    name = "youge"
    
    def change_name():
        
        global name
        name = "youyou"
    
    #     "youge"    "youyou"
    change_name()
    print(name)
    
    # output
    youyou

    簡単です.関数の内部でglobalでグローバル変数として宣言すればいいです.同様に、**関数にネストされた、すなわち閉パッケージ、装飾器などに対して、キーワードnonlocalにより関数内の変数を、関数外のEnclose層**として宣言することを実現する.
    name = "jack"
    
    def outer():
        name = "youge"
    
        #       local    
        def inner():
            name = "youyou"
            print("local:", name)
    
        inner()  #           name
        print("encolse:", name)
    
    
    print("global:", name)
    
    outer()
    
    
    # output
    global: jack
    local: youyou
    encolse: youge

    ここでinner関数(L層)でE層のnameを修正する、すなわちinnerでnameをnonlocalと宣言すればよい.
    name = "jack"
    
    def outer():
        name = "youge"
    
        #       local    
        def inner():
            nonlocal name
            name = "youyou"
            print("local:", name)
    
        inner()  #           name
        print("encolse:", name)
    
    
    print("global:", name)
    
    outer()
    
    
    # output 
    global: jack
    local: youyou  
    encolse: youyou

    関数ネストシーンでは、localレイヤでnameをnonlocalと宣言することでenclosedレイヤのnameを変更する.しかし、localレイヤでglobalを宣言すると効果がありません.なぜですか.まだ分かりませんが、実験です.
    ああ、突然貼りたいのですが、私はまだ菜鳥で、小さな間違いを犯しています.
    name = 'youge'
    def change_name():
        name = name +  "youyou"
    
    change_name()    
    print(name)
    
    # output
    UnboundLocalError: local variable 'name' referenced before assignment

    なぜなら、関数内部の空間において、nameは定義されていないからである.Pythonでは、関数プロセスの記憶は、再帰スタックによって実現する.スタックのFILOを利用する、(先進後出)の特徴は、1つの関数に出会うと、スタックで参加するメンバーを順次スタックに入れ、returnがあればスタック要素に置く.
    変数を定義してから使用しますか.Pythonの定義は、他の言語のタイプ宣言ではなく、インスタンスオブジェクトを指すことです.ここで最も混同しやすいです.
    nameをグローバル変数として変更し、関数パラメータで渡すことができます.
    
    #   1:            
    name = 'youge'
    
    def change_name(s):
        name = s +  "youyou"
        print(name)
    
    #              ,  "   ,    ")
    
    change_name(name)    
    
    # output
    yougeyouyou
    
    #   2:        ,    
    name = 'youge'
    
    
    def change_name():
        global name
        name = name + "youyou"
    
    
    change_name()
    print(name)
    
    # output
    yougeyouyou
    

    小結
  • 閉包、装飾器の本質は関数のネストであり、パラメータおよび関数が伝達できるのは、Pyhton変数の本質がポインタ
  • であるためである.
  • Pythonでは、変数名の競合をネーミングスペースで解決します.原理は、Linuxなどのコンピュータシステムのストレージファイルと同じ論理
  • です.
  • 変数名検索のルールは、Local->Enclosed->Global->Built-in
  • です.
  • 個人は理解できると思うが、グローバルとローカルの「相対性」でよい、またglobalとnonlocal(E層)で変数作用レベルを変えることができる.

  • 変数の役割ドメインは、ずっと使っていますが、よく無視しています.ここでまとめてみましょう.よくひっくり返すことはありません.役割ドメイン、ここまでにしましょう.