web-dev-qa-db-ja.com

関数内でクラスを作成し、含まれる関数のスコープで定義された関数にアクセスする

編集

この質問の下部にある私の完全な回答をご覧ください。

tl; dr answer:Pythonは静的にネストされたスコープを持っています。staticアスペクトは暗黙の変数宣言と相互作用し、非自明な結果をもたらします。

(これは、言語の一般的な動的性質のために特に驚く可能性があります)。

私はPythonのスコープルールをかなりうまく扱えると思っていましたが、この問題は徹底的に妨げられ、google-fuは失敗しました(驚いたことではありません-質問のタイトルを見てください;)

期待どおりに動作するいくつかの例から始めますが、ジューシーな部分については例4に進んでください。

例1。

_>>> x = 3
>>> class MyClass(object):
...     x = x
... 
>>> MyClass.x
3
_

簡単です:クラス定義中に、外側(この場合はグローバル)スコープで定義された変数にアクセスできます。

例2。

_>>> def mymethod(self):
...     return self.x
... 
>>> x = 3
>>> class MyClass(object):
...     x = x
...     mymethod = mymethod
...
>>> MyClass().mymethod()
3
_

繰り返しますが(現時点ではwhyこれを行いたい場合は無視します)、ここで予期しないことは何もありません。外側のスコープの関数にアクセスできます。

:フレデリックが以下に指摘したように、この関数は機能しないようです。代わりに、例5(以降)を参照してください。

例3。

_>>> def myfunc():
...     x = 3
...     class MyClass(object):
...         x = x
...     return MyClass
... 
>>> myfunc().x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myfunc
  File "<stdin>", line 4, in MyClass
NameError: name 'x' is not defined
_

これは基本的に例1と同じです。クラス定義内から外部スコープにアクセスしていますが、今回はmyfunc()のおかげでスコープはグローバルではありません。

編集5:@ user3022222は以下に指摘 として、私は元の投稿でこの例を破った。これが失敗するのは、関数(このクラス定義のような他のコードブロックではない)のみが、囲みスコープ内の変数にアクセスできるためです。非関数コードブロックの場合、ローカル変数、グローバル変数、および組み込み変数のみにアクセスできます。より詳細な説明は この質問 にあります。

もう一つ:

例4。

_>>> def my_defining_func():
...     def mymethod(self):
...         return self.y
...     class MyClass(object):
...         mymethod = mymethod
...         y = 3
...     return MyClass
... 
>>> my_defining_func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in my_defining_func
  File "<stdin>", line 5, in MyClass
NameError: name 'mymethod' is not defined
_

あの…すみません?

これが例2と何が違うのですか?

私は完全に困惑しています。整理してください。ありがとう!

追伸これが私の理解の問題ではないという偶然に、私はこれをPython 2.5.2およびPython 2.6.2残念ながら、現時点ではこれらはすべてアクセスできますが、どちらも同じ動作を示します。

Edithttp://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces に従って=:実行中はいつでも、名前空間に直接アクセスできるネストされたスコープが少なくとも3つあります。

  • 最初に検索される最も内側のスコープには、ローカル名が含まれます
  • 最も近い囲みスコープから検索される囲み関数のスコープには、非ローカル名だけでなく非グローバル名も含まれます
  • 最後から2番目のスコープには、現在のモジュールのグローバル名が含まれます
  • 最も外側のスコープ(最後に検索)は、組み込みの名前を含む名前空間です

#4。これらの2つ目の反例のようです。

Edit 2

例5

_>>> def fun1():
...     x = 3
...     def fun2():
...         print x
...     return fun2
... 
>>> fun1()()
3
_

Edit 3

@Frédéricが指摘したように、外側のスコープにあるのと同じ名前の変数への代入は、外側の変数を「マスク」して、代入が機能しないようにします。

したがって、例4のこの修正バージョンは機能します。

_def my_defining_func():
    def mymethod_outer(self):
        return self.y
    class MyClass(object):
        mymethod = mymethod_outer
        y = 3
    return MyClass

my_defining_func()
_

しかし、これはしません:

_def my_defining_func():
    def mymethod(self):
        return self.y
    class MyClass(object):
        mymethod_temp = mymethod
        mymethod = mymethod_temp
        y = 3
    return MyClass

my_defining_func()
_

私はまだ、このマスキングが発生する理由を完全には理解していません。割り当てが発生したときに名前バインディングが発生するべきではないのですか?

この例では、少なくともいくつかのヒント(およびより有用なエラーメッセージ)を提供します。

_>>> def my_defining_func():
...     x = 3
...     def my_inner_func():
...         x = x
...         return x
...     return my_inner_func
... 
>>> my_defining_func()()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in my_inner_func
UnboundLocalError: local variable 'x' referenced before assignment
>>> my_defining_func()
<function my_inner_func at 0xb755e6f4>
_

したがって、ローカル変数は関数の作成時に定義され(成功)、ローカル名が「予約」され、関数が呼び出されたときに外部スコープ名がマスクされるように見えます。

面白い。

答えてくれたフレデリックに感謝します!

参考のため、 python docs から:

スコープはテキストで決定されることを理解することが重要です。モジュールで定義された関数のグローバルスコープは、関数の呼び出し元またはエイリアスに関係なく、そのモジュールの名前空間です。一方、実際の名前の検索は実行時に動的に行われますが、言語定義は「コンパイル」時に静的な名前解決に向かって進化しているため、動的な名前解決に依存しないでください。 (実際、ローカル変数はすでに静的に決定されています。)

Edit 4

本当の答え

この一見紛らわしい動作は、Pythonの PEP 227で定義されている静的にネストされたスコープ が原因です。実際には PEP 3104 とは関係ありません。

PEP 227から:

名前解決規則は、静的スコープの言語では一般的です[...] [例外]変数は宣言されません。名前バインディング操作が関数内のどこかで発生した場合、その名前は関数に対してローカルとして扱われ、すべての参照はローカルバインディングを参照します。名前がバインドされる前に参照が発生すると、NameErrorが発生します。

[...]

Tim Petersの例は、宣言がない場合のネストされたスコープの潜在的な落とし穴を示しています。

_i = 6
def f(x):
    def g():
        print i
    # ...
    # skip to the next page
    # ...
    for i in x:  # ah, i *is* local to f, so this is what g sees
        pass
    g()
_

g()の呼び出しは、forループによってf()でバインドされた変数iを参照します。Ifg()はループが実行される前に呼び出され、NameErrorが発生します。

Timの例の2つの単純なバージョンを実行してみましょう。

_>>> i = 6
>>> def f(x):
...     def g():
...             print i
...     # ...
...     # later
...     # ...
...     i = x
...     g()
... 
>>> f(3)
3
_

g()が内部スコープでiを見つけられない場合、動的に外側を検索し、iのスコープでfを見つけます。 _3_から_i = x_割り当てまで。

ただし、fの最後の2つのステートメントの順序を変更すると、エラーが発生します。

_>>> i = 6
>>> def f(x):
...     def g():
...             print i
...     # ...
...     # later
...     # ...
...     g()
...     i = x  # Note: I've swapped places
... 
>>> f(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in f
  File "<stdin>", line 3, in g
NameError: free variable 'i' referenced before assignment in enclosing scope
_

PEP 227が「名前解決規則は静的スコープ言語の典型である」と述べたことを思い出して、(半)同等のCバージョンの提供を見てみましょう:

_// nested.c
#include <stdio.h>

int i = 6;
void f(int x){
    int i;  // <--- implicit in the python code above
    void g(){
        printf("%d\n",i);
    }
    g();
    i = x;
    g();
}

int main(void){
    f(3);
}
_

コンパイルして実行:

_$ gcc nested.c -o nested
$ ./nested 
134520820
3
_

そのため、Cは喜んで非バインド変数を使用しますが(この場合、134520820に以前に格納されていたものを使用)、Python(ありがたいことに)拒否します。

興味深い補足として、静的にネストされたスコープは、 Alex Martelliが呼んだ "Pythonコンパイラーが行う単一の最も重要な最適化:関数のローカル変数は保持されません。口述では、それらは値のタイトなベクトル内にあり、各ローカル変数アクセスは名前ルックアップではなく、そのベクトル内のインデックスを使用します。」

60
Gabriel Grant

これは、Pythonの名前解決ルールの成果物です。 グローバルスコープとローカルスコープにのみアクセスできますが、その間のスコープにはアクセスできません。すぐ外側のスコープではありません。

EDIT:上記の表現は不十分で、doは外部スコープで定義された変数にアクセスできますが、しかし、非グローバル名前空間から_x = x_または_mymethod = mymethod_を実行することにより、実際にはローカルで定義している変数で外部変数をマスクしています。

例2では、​​直接外部スコープはグローバルスコープなので、MyClassmymethodを見ることができますが、例4では、直接外部スコープはmy_defining_func()なので、なぜなら、mymethodの外側の定義は、そのローカル定義によってすでにマスクされているからです。

非ローカル名前解決の詳細については、 PEP 3104 を参照してください。

また、上記で説明した理由により、例3をPython 2.6.5または3.1.2で実行することはできません。

_>>> def myfunc():
...     x = 3
...     class MyClass(object):
...         x = x
...     return MyClass
... 
>>> myfunc().x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myfunc
  File "<stdin>", line 4, in MyClass
NameError: name 'x' is not defined
_

ただし、次のように機能します。

_>>> def myfunc():
...     x = 3
...     class MyClass(object):
...         y = x
...     return MyClass
... 
>>> myfunc().y
3
_
20

この投稿は数年前ですが、Pythonのスコープと静的バインディングの重要な問題を議論することはまれです。ただし、例3のように、読者を混乱させる可能性のある著者の重要な誤解があります。 (他のものがすべて正しいことを当然のこととは思わないでください。例3で提起された問題だけを詳細に見ただけです)。何が起こったのかを明確にしましょう。

例3では

def myfunc():
    x = 3
    class MyClass(object):
        x = x
    return MyClass

>>> myfunc().x

投稿の著者が言ったこととは異なり、エラーを返す必要があります。例1ではxがグローバルスコープの3に割り当てられたため、彼はエラーを見逃したと思います。したがって、何が起こったのか間違った理解。

説明はこの投稿で詳しく説明されています Pythonで変数への参照が解決される方法

6
user3022222