Python変数作用域LEGB
6317 ワード
レビュー-Decorator
前編では、閉包と装飾器の概念について述べた.閉包とは、関数内部にネストする関数である.装飾器はただ閉包する特殊なシーンで、特殊はもし外の関数のパラメータが1つを指すならば、装飾される関数の住所に用いる時(必ずしも住所ではありませんて、勝手に良いです)、"@xxx"のこのような書き方があって、やはりとても面白いです.装飾器の役割は、元の関数のコードを変更しない前提の下で、元の関数に新しい機能を追加することである.書き方からすると、やはり簡潔で優雅だ.
装飾器の通俗的な書き方
飾りに参列する
このような「@」の書き方は、外関数のパラメータがfuncアドレスであることを要求するが、パラメータを伝達できるようにするには、外パンに関数を1層(パラメータを受け入れる役割)すれば、作用空間を拡大し、パラメータを得ることになるのではないか.
この種のデザイナがパラメータを伝達するアプリケーションシーンは、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."(ほとんどのネーミングスペースは辞書で実現されます)
すなわち、ネーミングスペースは、変数のネーミング競合を回避するための制約である.それぞれのネーミング空間は互いに独立しており、一つの空間では再名することはできず、異なる空間では関係ない.コンピュータシステムと同じ論理でファイルを格納します.内蔵スペース:(built-in names):Python内蔵関数、異常クラスなどのPython内蔵名... グローバルスペース:(global names):定数、モジュールで定義された名前(クラス、インポートモジュール)... Enclosed:関数にネストされている可能性のある関数など... ローカル名:(local names):関数で定義された名前(関数内の変数)...
Python検索変数は、Local->Enclosed->Global->Built-inの順です.
実は、私の個人的な経験から言えば、局部と全局の相対性を区別することができます.基本的には直感的には、コードを書くpyファイルを例に挙げる.最外層には、変数、クラス定義、関数定義、fromから...import .. の変数あるいは関数名、これらはグローバル変数で、最も外のクラスあるいは関数、中はそれぞれの名前の空間です.
実はよく理解しています.上段codeについては、L-E-G-Bの法則により、一つの相対を理解すればよい.
グローバルvsローカル
局所はグローバルを変えることはなく、局所内でグローバル変数を得ることができることがわかる.さもないと閉包して、外関数が受信したパラメータ、内関数はどのように手に入れることができますか?外の関数で、“拡充しました”内の関数の作用域、L->E->G->Bの法則によって探し当てます.
globalとnonlocal
改めることができなかったことに気づいたのは自然だ.なぜなら、関数を呼び出すとき、中のnameはLocal変数であり、グローバルなnameには影響しないので、関数の内部でグローバル変数を変更したい場合は、その変数をglobalキーワードで宣言すればよい.
簡単です.関数の内部でglobalでグローバル変数として宣言すればいいです.同様に、**関数にネストされた、すなわち閉パッケージ、装飾器などに対して、キーワードnonlocalにより関数内の変数を、関数外のEnclose層**として宣言することを実現する.
ここでinner関数(L層)でE層のnameを修正する、すなわちinnerでnameをnonlocalと宣言すればよい.
関数ネストシーンでは、localレイヤでnameをnonlocalと宣言することでenclosedレイヤのnameを変更する.しかし、localレイヤでglobalを宣言すると効果がありません.なぜですか.まだ分かりませんが、実験です.
ああ、突然貼りたいのですが、私はまだ菜鳥で、小さな間違いを犯しています.
なぜなら、関数内部の空間において、nameは定義されていないからである.Pythonでは、関数プロセスの記憶は、再帰スタックによって実現する.スタックのFILOを利用する、(先進後出)の特徴は、1つの関数に出会うと、スタックで参加するメンバーを順次スタックに入れ、returnがあればスタック要素に置く.
変数を定義してから使用しますか.Pythonの定義は、他の言語のタイプ宣言ではなく、インスタンスオブジェクトを指すことです.ここで最も混同しやすいです.
nameをグローバル変数として変更し、関数パラメータで渡すことができます.
小結閉包、装飾器の本質は関数のネストであり、パラメータおよび関数が伝達できるのは、Pyhton変数の本質がポインタ であるためである. Pythonでは、変数名の競合をネーミングスペースで解決します.原理は、Linuxなどのコンピュータシステムのストレージファイルと同じ論理 です.変数名検索のルールは、Local->Enclosed->Global->Built-in です.個人は理解できると思うが、グローバルとローカルの「相対性」でよい、またglobalとnonlocal(E層)で変数作用レベルを変えることができる.
変数の役割ドメインは、ずっと使っていますが、よく無視しています.ここでまとめてみましょう.よくひっくり返すことはありません.役割ドメイン、ここまでにしましょう.
前編では、閉包と装飾器の概念について述べた.閉包とは、関数内部にネストする関数である.装飾器はただ閉包する特殊なシーンで、特殊はもし外の関数のパラメータが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)]
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
小結
変数の役割ドメインは、ずっと使っていますが、よく無視しています.ここでまとめてみましょう.よくひっくり返すことはありません.役割ドメイン、ここまでにしましょう.