[解決済み] 関数内にクラスを作成し、包含関数のスコープで定義された関数にアクセスする
質問
編集 :
この質問の下にある私の全回答を参照してください。
tl;drの答え : Pythonは静的にネストされたスコープを持ちます。そのため 静的 アスペクトは暗黙の変数宣言と相互作用し、明白でない結果をもたらす可能性があります。
(これは言語の一般的な動的性質のために特に驚くべきことかもしれません)。
私は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
もう一度(とりあえず無視して なぜ を無視して)、ここでは何も予想外のことはありません。
注意 Frédéric が指摘するように、この関数は動作しないようです。代わりに例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 さんのご指摘の通り のように、私は最初の投稿でこの例を失敗させました。これは、関数だけが (このクラス定義のような他のコードブロックではなく) 囲んでいるスコープ内の変数にアクセスできるために失敗したのだと思います。関数以外のコードブロックでは、ローカル変数、グローバル変数、組み込み変数のみにアクセス可能です。より詳細な説明は この質問
もう1つ
例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と何が違うのでしょうか?
完全に混乱しています。解決してください。 ありがとうございます。
P.S. 私の理解の問題だけではない可能性もありますが、私はこれを Python 2.5.2 と Python 2.6.2 で試しました。残念ながら、これらは私が現在アクセスできるすべてですが、それらは両方とも同じ挙動を示します。
編集 によると http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces によると、実行中のいつでも、名前空間に直接アクセスできる少なくとも3つのネストされたスコープが存在します。
- 最も内側のスコープで、最初に検索されます。 最初に検索される最も内側のスコープには、ローカル の名前
- を囲むすべての 関数のスコープがあり、それは 最も近い スコープには、ローカルでない名前だけでなく 非グローバル名
- 最後から 2 番目のスコープには 現在のモジュールのグローバル名
- 一番外側のスコープ (最後に検索されます) を含む名前空間です。 の名前です。
#4.は、このうち2番目の反例と思われます。
編集2
例5.
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
3を編集
Frédéric が指摘したように、外部スコープで持っているのと同じ名前の変数への代入は、外部変数を "mask" して、代入が機能するのを妨げているようです。
というわけで、例 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>
つまり、ローカル変数は関数作成時に定義され (これは成功します)、その結果ローカル名が "reserved" となり、関数が呼び出されたときに外部スコープ名がマスクされるようです。
興味深いですね。
Frédéricさん、回答ありがとうございました。
参考までに、以下より pythonのドキュメント :
<ブロッククオートスコープがテキストで決定されることを理解することは重要です。 はテキストで決定されることを理解することが重要です。 モジュールで定義された関数のグローバルスコープは モジュールで定義された関数のグローバルスコープは、そのモジュールの名前空間です。 モジュールで定義された関数のグローバルスコープはそのモジュールの名前空間であり、その関数がどこから、あるいはどのようなエイリアスで呼ばれたとしても 関数が呼び出されたとしても、そのグローバルスコープはそのモジュールの名前空間です。一方 実際の名前の検索は実行時に動的に行われます 実行時に動的に行われる。 言語定義は、静的な名前解決に向かって進化しています。 しかし、言語定義は静的な名前解決に向かって進化しています。 「動的な名前解決に頼らないでください。 動的な名前解決に頼ってはいけません。(実際 ローカル変数はすでに静的に決定されています 静的に決定されます)。
4を編集
本当の答え
この一見わかりにくい挙動は、Pythonの の静的ネストされたスコープが原因です。 . とは何の関係もありません。 PEP 3104 .
PEP227より。
名前解決のルールは典型的なものです。 静的スコープを持つ言語の典型的なものです [...]. [ただし] 変数は宣言されない。 名前バインディング操作が関数内のどこかで発生した場合 関数内の任意の場所で名前バインディング操作が発生した場合、その名前 はその関数のローカルなものとして扱われ として扱われ,すべての参照はそのローカルな を参照します。 名前がバインドされる前に参照が発生した場合 が発生した場合、NameErrorが発生します。 が発生します。
[...]
Tim Peters 氏の例では、宣言がない場合にスコープを入れ子にすることの潜在的な落とし穴が示されています。 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()の呼び出しは、f()でforループによって束縛された変数iを参照することになります。 ループによってf()に束縛された変数iを参照します。 ループが実行される前にg()が呼び出された場合、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
の代入が必要です。
の最後の2つの文の順番を変えることで
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
PEP227に「名前解決のルールは静的スコープを持つ言語の典型である」と書かれていたことを思い出し、(半)等価な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言語では結合されていない変数を喜んで使いますが、Pythonでは(ありがたいことに)拒否されます。
面白い余談ですが、静的にネストされたスコープによって Alex Martelli が呼び出した 関数のローカル変数はディクショナリーではなく、値のタイトなベクトルに保持され、各ローカル変数へのアクセスは名前検索ではなく、そのベクトルのインデックスを使用します。
どのように解決するのですか?
それはPythonの名前解決ルールのせいです。
の場合、グローバルスコープとローカルスコープにしかアクセスできず、その中間のスコープ、例えば自分のすぐ外側のスコープにはアクセスできません。
EDITです。
上記は言葉足らずでしたね、あなた
する
は外部スコープで定義された変数にアクセスすることができますが
x = x
または
mymethod = mymethod
をグローバルでない名前空間から呼び出した場合、実際には外側の変数をローカルに定義している変数でマスクしていることになります。
例 2 では、直前の外部スコープがグローバルスコープであるため
MyClass
を見ることができます。
mymethod
と表示されますが、例4では、直前の外部スコープが
my_defining_func()
の外側で定義されているので、それはできません。
mymethod
はすでにそのローカル定義によってマスクされているため、できません。
参照 PEP 3104 を参照してください。
また、上記の理由により、Python 2.6.5 または 3.1.2 のいずれでも example 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
しかし、次のようにすればうまくいくでしょう。
>>> def myfunc():
... x = 3
... class MyClass(object):
... y = x
... return MyClass
...
>>> myfunc().y
3
関連
-
[解決済み] 最小限の驚き」と「変更可能なデフォルトの引数
-
[解決済み] オブジェクトの現在のプロパティと値をすべて表示する組み込み関数はありますか?
-
[解決済み] 同じディレクトリやサブディレクトリ内のクラスをインポートする方法は?
-
[解決済み】ローカル変数の初回使用後に再代入すると、UnboundLocalErrorが発生する。
-
[解決済み] DataFrameの文字列、dtypeがobjectの場合
-
[解決済み] PythonでのAWS Lambdaのインポートモジュールエラー
-
[解決済み] Pythonです。未束縛のメソッドを束縛する?
-
[解決済み] Jupyter (IPython)ノートブックのセッションをpickleして保存する方法
-
[解決済み] PyQtアプリケーションのスレッド化。QtスレッドとPythonスレッドのどちらを使うか?
-
[解決済み] データクラスとtyping.NamedTupleの主な使用例
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 現在のモジュールでsetattr()を呼び出すにはどうすればよいですか?
-
[解決済み] Flaskで1時間ごとに関数を実行するようにスケジュールするには?
-
[解決済み] PythonでSVGからPNGに変換する
-
[解決済み] Djangoで2つの日付の間を選択する
-
[解決済み] Pythonのargparseを使った隠し引数の作成
-
[解決済み] PyQtアプリケーションのスレッド化。QtスレッドとPythonスレッドのどちらを使うか?
-
[解決済み] djangoフレームワークでフォームフィールドから値を取得するには?
-
[解決済み] Pythonの文字列の前にあるbという接頭辞は何を意味するのですか?
-
[解決済み] PythonのRequestsモジュールを使ってWebサイトに "ログイン "するには?
-
[解決済み] 新しいpip backtrackingの実行時問題の解決