スコーピングルールの簡単な説明?


472

Pythonスコーピングルールとは正確には何ですか?

コードがある場合:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

どこにありxますか?可能な選択肢には、以下のリストが含まれます。

  1. 囲んでいるソースファイル
  2. クラスの名前空間
  3. 関数定義内
  4. forループのインデックス変数
  5. forループの内部

また、実行中に、関数spamがどこかに渡された場合のコンテキストもあります。そして、おそらくラムダ関数は少し異なって渡しますか?

どこかに単純な参照またはアルゴリズムがあるはずです。中級のPythonプログラマーにとって混乱する世界です。


2
スコーピングルールは、Pythonドキュメントdocs.python.org/3/reference/…でかなり簡潔に(ただし完全に)記述されています。
jefe2000

回答:


420

実際には、Pythonスコープ解決のための簡潔なルール、Learning Python、3番目。エド。。(これらのルールは、属性ではなく変数名に固有です。ピリオドなしで参照すると、これらのルールが適用されます。)

LEGBルール

  • L ocal —関数(defまたはlambda)内で何らかの方法で割り当てられ、その関数でグローバルに宣言されていない名前

  • E nclosing-function — 内部から外部まで、静的に囲むすべての関数(defまたはlambda)のローカルスコープで割り当てられた名前

  • Gのローブ(モジュール) -モジュール・ファイルの最上位レベルに割り当てられた名前、または実行することglobalで文をdefファイル内

  • Bの: - (パイソン)uilt-に名前は組み込みの名前のモジュールに事前に割り当てられopenrangeSyntaxError、など

だから、の場合

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

forループは、独自の名前空間を持っていません。LEGBの順序では、スコープは

  • L:ローカルでdef spam(でcode3code4code5
  • E:任意の囲み関数(例全体が別の例に含まれていた場合def
  • G:xモジュール(code1)でグローバルに宣言されたものはありますか?
  • B:xPythonに組み込まれているもの。

xcode2(あなたがそれを期待するかもしれない場合でも、Anttiの答えまたはここを参照してください)には決して見つかりません。


45
グローバルアクセスへの警告として-グローバル変数の読み取りは明示的な宣言なしに発生する可能性がありますが、グローバル(var_name)を宣言せずにそれに書き込むと、代わりに新しいローカルインスタンスが作成されます。
Peter Gibson

12
実際には@Peter global(var_name)は構文的に正しくありません。正しい構文はglobal var_name括弧なしです。あなたには有効なポイントがあります。
martineau

もしそうなら、なぜfooの "y"変数が下の "bar"に表示されないのですか: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer

3
@ジョナサン:それぞれyが書き込まれていて、global y宣言がないため-@Peterのコメントを参照してください。
martineau 2013

@LakshmanPrasadこれは「E」に該当しますが、特筆すべき特別な動作が1つあります。これはクラス変数であるため、オブジェクト間で「グローバル」です。それに割り当てるとします、あなたがやっているかわからない場合は、デバッグの問題への予期せぬハードにつながります。
Ctrl-C

157

基本的に、Pythonで新しいスコープを導入するのは関数定義だけです。クラスは、本体で直接定義されたものはすべてクラスの名前空間に配置されるという少し特殊なケースですが、クラスに含まれるメソッド(またはネストされたクラス)から直接アクセスすることはできません。

あなたの例では、xが検索されるスコープは3つだけです:

  • スパムのスコープ-code3とcode5で定義されたすべてを含む(ループ変数code4と同様)

  • グローバルスコープ-code1で定義されたすべてとFoo(およびその後のすべての変更)を含む

  • 組み込みの名前空間。少し特殊なケース-これには、len()やstr()などのさまざまなPython組み込み関数と型が含まれています。通常、これはユーザーコードによって変更されるべきではないので、標準の関数のみを含み、それ以外は何も含まないことを期待してください。

ネストされた関数(またはラムダ)を画像に導入した場合にのみ、より多くのスコープが表示されます。ただし、これらは期待どおりに動作します。ネストされた関数は、ローカルスコープ内のすべてのもの、およびそれを囲む関数のスコープ内のすべてにアクセスできます。例えば。

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

制限:

ローカル関数の変数以外のスコープ内の変数にはアクセスできますが、構文を追加しないと新しいパラメーターに再バインドできません。代わりに、割り当てにより、親スコープの変数に影響を与える代わりに、新しいローカル変数が作成されます。例えば:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

関数スコープ内からグローバル変数のバインディングを実際に変更するには、グローバルキーワードを使用して変数がグローバルであることを指定する必要があります。例えば:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

現在、囲んでいる関数スコープ内の変数に対して同じことを行う方法はありませんが、Python 3では、nonlocalグローバルと同じように機能する新しいキーワード「」が導入されていますが、関数スコープがネストされています。


111

Python3の時間に関して完全な答えはなかったので、ここで答えを出しました。ここで説明されていることのほとんどは、4.2.2 Python 3ドキュメントの名前解決で詳しく説明されています。

他の回答で提供されているように、LEGBには、Local、Enclosing、Global、Builtinの4つの基本スコープがあります。これらに加えて、特別なスコープであるクラス本体があります。これは、クラス内で定義されたメソッドの囲みスコープを構成しません。クラス本体内の割り当てにより、そこからの変数がクラス本体にバインドされます。

特に、および以外ブロックステートメントは、変数スコープを作成しません。Python 2ではリスト内包表記は変数スコープを作成しませんが、Python 3ではリスト内包表記内のループ変数は新しいスコープ内に作成されます。defclass

クラス本体の特性を示すため

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

したがって、関数本体とは異なり、変数をクラス本体の同じ名前に再割り当てして、同じ名前のクラス変数を取得できます。この名前をさらに検索すると、代わりにクラス変数に解決されます。


Pythonを初めて使用する多くの人にとって大きな驚きの1つは、forループが変数スコープを作成しないことです。Python 2では、リスト内包表記もスコープを作成しません(一方、ジェネレーターとdict内包表記は作成します!)代わりに、関数またはグローバルスコープの値をリークします。

>>> [ i for i in range(5) ]
>>> i
4

内包表記は、Python 2のラムダ式内で変更可能な変数を作成するための狡猾な(または必要であればひどい)方法として使用できます。ラムダ式は、defステートメントと同様に変数スコープを作成しますが、ラムダ内ではステートメントを使用できません。Pythonのステートメントである代入は、ラムダでの変数の代入は許可されないことを意味しますが、リスト内包表記は式です...

この動作はPython 3で修正されました-内包表現やジェネレーターが変数をリークすることはありません。


グローバルは本当にモジュールのスコープを意味します。主要なPythonモジュールは__main__です。インポートされたすべてのモジュールには、sys.modules変数を介してアクセスできます。へのアクセスを取得するに__main__sys.modules['__main__']、またはを使用できますimport __main__。そこで属性にアクセスして割り当てることは完全に許容されます。それらは、メインモジュールのグローバルスコープの変数として表示されます。


現在のスコープ(クラススコープを除く)で名前が割り当てられている場合、その名前はそのスコープに属していると見なされます。それ以外の場合は、変数に割り当てられているスコープに属していると見なされます(割り当てられない場合があります)まだ、またはまったくない)、または最後にグローバルスコープ。変数がローカルと見なされているが、まだ設定されていないか、削除されている場合、変数の値を読み取るUnboundLocalErrorと、のサブクラスであるが生成されNameErrorます。

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

スコープはグローバル(モジュールスコープ)変数を明示的に変更することを宣言し、グローバルキーワードを使用します。

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

これは、囲んでいるスコープでシャドウされていても可能です。

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

Python 2では、囲んでいるスコープの値を変更する簡単な方法はありません。通常、これは、長さが1のリストなど、変更可能な値を持つことでシミュレートされます。

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

しかしpython 3では、nonlocalこれが救いに来ます:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

nonlocalドキュメントはと言います

非ローカルステートメントにリストされている名前は、グローバルステートメントにリストされている名前とは異なり、囲んでいるスコープ内の既存のバインディングを参照する必要があります(新しいバインディングを作成するスコープを明確に決定することはできません)。

つまり、nonlocal常に名前がバインドされている(つまり、割り当てられている、forターゲット変数として、with句内で、または関数パラメーターとして使用されている)最も内側の外側の非グローバルスコープを指します。


現在のスコープに対してローカルであると見なされない変数、またはそれを囲むスコープは、グローバル変数です。グローバル名はモジュールのグローバル辞書で検索されます。見つからない場合は、組み込みモジュールからグローバルが検索されます。モジュールの名前がpython 2からpython 3に変更されました。Python 2ではそれが__builtin__あり、Python 3ではそれが呼び出されbuiltinsます。ビルトインモジュールの属性に割り当てる場合、そのモジュールが同じ名前の独自のグローバル変数でそれらをシャドウしない限り、その後、読み取り可能なグローバル変数として任意のモジュールに表示されます。


組み込みモジュールを読むことも役立ちます。ファイルの一部にpython 3スタイルの印刷関数が必要だが、ファイルの他の部分がまだprintステートメントを使用しているとします。Python 2.6-2.7では、次のようにしてPython 3 print関数を取得できます。

import __builtin__

print3 = __builtin__.__dict__['print']

は、from __future__ import print_function実際にはprintPython 2のどこにも関数をインポートしません。代わりprintに、現在のモジュールのステートメントの解析ルールを無効にし、print他の変数識別子と同様に処理printして、関数を組み込み関数で検索できるようにします。


23

Python 2.xのスコープ規則は、他の回答ですでに概説されています。追加する唯一のことは、Python 3.0には非ローカルスコープの概念もあります( 'nonlocal'キーワードで示されます)。これにより、外側のスコープに直接アクセスできるようになり、レキシカルクロージャーなどの巧妙なトリックを実行できるようになります(変更可能なオブジェクトを含む醜いハックは不要です)。

編集:これに関する詳細は、PEPです。


23

スコープのもう少し完全な例:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

出力:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200

6
これは素晴らしい答えです。ただし、との違いを強調する必要があるmethodと思いmethod_local_refます。methodは、グローバル変数にアクセスして、のように出力できます5. Global x。しかしmethod_local_ref、後で同じ名前のローカル変数を定義しているためです。これをテストして、x = 200行を削除して違いを確認できます
kiril

@brianray:zはどうですか?
Malik

@kirilそのことについてのメモを追加しました
brianray 2017年

@ MalikA.Rumi面白くないのでzを削除しました
brianray 2017年

驚いたことに、これはPythonスコープの唯一の明確な説明であり、私はすべてのSOで見つけることができました。非常に基本的な例を使用するだけです。ありがとう!
not2qubit 2018年

13

Pythonは、通常、3つの名前空間を使用して変数を解決します。

実行中はいつでも、名前空間に直接アクセスできるネストされたスコープが少なくとも3つあります。最初に検索される最も内側のスコープには、ローカル名が含まれます。最も近い囲みスコープから開始して検索される、囲み関数の名前空間。次に検索される中央のスコープには、現在のモジュールのグローバル名が含まれます。そして、最も外側のスコープ(最後に検索されたもの)は、組み込み名を含む名前空間です。

二つの機能があります。globalsそしてlocalsこのことは、これらの名前空間の2あなたの内容を示しています。

名前空間は、パッケージ、モジュール、クラス、オブジェクトの構築、および関数によって作成されます。他の種類の名前空間はありません。

この場合、named関数の呼び出しxは、ローカル名前空間またはグローバル名前空間で解決する必要があります。

この場合のローカルは、メソッド関数の本体ですFoo.spam

グローバルは-まあ-グローバルです。

ルールは、メソッド関数(およびネストされた関数定義)によって作成されたネストされたローカルスペースを検索してから、グローバルを検索することです。それでおしまい。

他のスコープはありません。for声明(など他の複合文ifとはtry)新しいネストされたスコープを作成しないでください。定義のみ(パッケージ、モジュール、関数、クラス、オブジェクトインスタンス)

クラス定義内では、名前はクラス名前空間の一部です。 code2たとえば、クラス名で修飾する必要があります。一般的にFoo.code2。ただし、self.code2Pythonオブジェクトは含まれているクラスをフォールバックと見なすため、動作します。

オブジェクト(クラスのインスタンス)にはインスタンス変数があります。これらの名前はオブジェクトの名前空間にあります。それらはオブジェクトによって修飾される必要があります。(variable.instance。)

クラスメソッド内から、ローカルとグローバルがあります。self.variableインスタンスを名前空間として選択するとします。これselfは、すべてのクラスメンバー関数の引数であり、ローカル名前空間の一部になることに注意してください。

PythonスコープルールPythonスコープ変数スコープを参照してください。


5
これは時代遅れです。2.1(7年前)以降、2つ以上のスコープがあります。ネストされた関数は新しいスコープを導入するため、関数内の関数はローカルスコープ、それを囲む関数スコープ、およびグローバルスコープ(組み込み)にアクセスできます。
ブライアン、

すみません、これはもう当てはまりません。Python has two namespaces available. Global and local-to-something.
リズワンカシム

9

xはどこにありますか?

まだ定義していないため、xは見つかりません。:-)そこに置くと、code1(グローバル)またはcode3(ローカル)で見つかります。

code2(クラスメンバー)は、同じクラスのメソッド内のコードからは見えません。通常は、selfを使用してアクセスします。code4 / code5(ループ)はcode3と同じスコープに存在するため、そこでxに書き込んだ場合、新しいxを作成するのではなく、code3で定義されたxインスタンスを変更します。

Pythonは静的にスコープが設定されているため、「spam」を別の関数に渡しても、spamはそれが由来するモジュール(code1で定義)のグローバル、および他のすべての包含スコープ(以下を参照)に引き続きアクセスできます。code2メンバーは再びselfを介してアクセスされます。

ラムダはデフと違いはありません。関数内でラムダを使用する場合、ネストされた関数を定義するのと同じです。Python 2.2以降では、ネストされたスコープを使用できます。この場合、関数のネストのどのレベルでもxをバインドでき、Pythonは最も内側のインスタンスを取得します。

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3は、最も近い包含スコープからインスタンスxを参照します。これは、fun2に関連付けられた関数スコープです。ただし、fun1でグローバルに定義されている他のxインスタンスは影響を受けません。

nested_scopesの前— Python 2.1より前、および2.1ではfrom-future-importを使用して機能を具体的に要求しない限り、fun1およびfun2のスコープはfun3に表示されないため、S.Lottの答えは保持され、グローバルxを取得します:

0 0

1

Pythonでは、

値が割り当てられる変数は、割り当てが表示されるブロックに対してローカルです。

現在のスコープで変数が見つからない場合は、LEGBの順序を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.