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

ガブリエルグラント:

編集

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

tl; dr answer: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

繰り返しますが(今のところこれを実行する理由を無視します)、ここでは予期しないことは何もありません。外側のスコープの関数にアクセスできます。

:フレデリックが下で指摘したように、この関数は機能していないようです。代わりに例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と何が違うのですか?

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

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

編集によるhttp://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces:実行中の任意の時点で、その名前空間に直接アクセス可能な少なくとも三つのネストされたスコープがあります。

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

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

編集2

例5。

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

編集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から

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

編集4

本当の答え

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

PEP 227から:

名前解決規則は、静的スコープの言語で一般的です[...] [except]変数は宣言されていません。名前バインディング操作が関数のどこかで発生した場合、その名前は関数のローカルとして扱われ、すべての参照はローカルバインディングを参照します。名前がバインドされる前に参照が発生すると、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を参照します。ループが実行される前にg()が呼び出されると、NameErrorが発生します。

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

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

内部スコープでg()見つからない場合i、動的に外側を検索し、割り当てを通じてバインドされているiin fのスコープを見つけます。3i = x

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

>>> 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はバインドされていない変数を(喜んで以前にそこに格納されていたものを使用して)使用しますが、Pythonは(ありがたいことに)拒否します。

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

フレデリックハミディ:

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

EDIT:上記の悪い言葉で表現された、あなたがやる外側のスコープで定義された変数へのアクセス権を持っていますが、実行してx = xまたはmymethod = mymethod非グローバル名前空間から、あなたが実際にあなたがローカルに定義している1と外側の変数をマスキングしています。

例2では、​​直接外側のスコープはグローバルスコープであるため、MyClassを表示できますmymethodが、例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

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

関数は、それ自体の親関数で定義された変数にアクセスできますか?

分類Dev

PHP:関数スコープ内で定義されたすべての変数をクエリします

分類Dev

関数内(クラス内)の変数は、それが作成された(および以前に定義された)関数でのみ使用できます。

分類Dev

別の関数内で定義された関数をウィンドウスコープに含めるにはどうすればよいですか?

分類Dev

Angularディレクティブの分離されたスコープ内で関数を定義する

分類Dev

ネストされた関数定義とヒアドキュメントを含む関数をbashでエクスポートする

分類Dev

関数で定義された変数にアクセスします

分類Dev

事前定義された関数を呼び出して、その関数内にある変数のプロパティにアクセスするにはどうすればよいですか?

分類Dev

ラムダ関数を含むコールスタックが与えられた場合、そのソースをどのように決定できますか?

分類Dev

クラス外のPythonクラス関数ですが、クラス内で定義された関数を呼び出します

分類Dev

C ++の関数内のstructで定義された列挙型クラスのプロパティにアクセスします

分類Dev

Pythonで関数を定義した後、関数スコープ内の変数を変更するにはどうすればよいですか?

分類Dev

関数内で作成された変数に、その関数の外部でswift 4でアクセスすることは可能ですか?

分類Dev

関数内で定義された構造体から関数のコンテンツにアクセスする

分類Dev

関数のvarで定義されたクラスを変更します

分類Dev

Pythonクロージャ:関数内で定義された関数は、1レベル上の変数にアクセスできますか?

分類Dev

関数内の変数にアクセスします。これは、テストデータで使用するモデルを定義します。Tensorflow

分類Dev

bash関数でローカル配列を定義し、その関数の外部でアクセスします

分類Dev

別の関数でグローバルに定義された変数の関数でデータセットにアクセスする

分類Dev

そのクラスのオブジェクトが作成されたときにコンストラクターがメモリを取得する外部で関数がクリアされていますか。関数が呼び出されていません

分類Dev

リストを含むデータ型を作成し、それらに関数を定義する

分類Dev

自己内で定義された関数を使用してクラス変数を更新します

分類Dev

リアクティブ関数から作成されたデータにアクセスして、ShinyでreactiveValuesを定義します

分類Dev

関数が、定義された文字列ではなく、パラメータとしてそれ自体を含むクラスを必要とするのはなぜですか?

分類Dev

別の関数で定義されているアクセス関数

分類Dev

Pythonモジュールでアンダースコアのようなアクセス修飾子を使用すると、定義された変数またはインポートされたモジュール、関数、クラスにアクセスする方法が制御されますか?

分類Dev

関数内で作成されたオブジェクトのスコープ-参照が関数の外部に保存およびアクセスされた場合、それらは有効ですか?

分類Dev

コンストラクター関数の新しく作成されたonjectでゲッター関数を定義するにはどうすればよいですか?

分類Dev

基本クラスで定義された関数を子クラスでオーバーライドする

Related 関連記事

  1. 1

    関数は、それ自体の親関数で定義された変数にアクセスできますか?

  2. 2

    PHP:関数スコープ内で定義されたすべての変数をクエリします

  3. 3

    関数内(クラス内)の変数は、それが作成された(および以前に定義された)関数でのみ使用できます。

  4. 4

    別の関数内で定義された関数をウィンドウスコープに含めるにはどうすればよいですか?

  5. 5

    Angularディレクティブの分離されたスコープ内で関数を定義する

  6. 6

    ネストされた関数定義とヒアドキュメントを含む関数をbashでエクスポートする

  7. 7

    関数で定義された変数にアクセスします

  8. 8

    事前定義された関数を呼び出して、その関数内にある変数のプロパティにアクセスするにはどうすればよいですか?

  9. 9

    ラムダ関数を含むコールスタックが与えられた場合、そのソースをどのように決定できますか?

  10. 10

    クラス外のPythonクラス関数ですが、クラス内で定義された関数を呼び出します

  11. 11

    C ++の関数内のstructで定義された列挙型クラスのプロパティにアクセスします

  12. 12

    Pythonで関数を定義した後、関数スコープ内の変数を変更するにはどうすればよいですか?

  13. 13

    関数内で作成された変数に、その関数の外部でswift 4でアクセスすることは可能ですか?

  14. 14

    関数内で定義された構造体から関数のコンテンツにアクセスする

  15. 15

    関数のvarで定義されたクラスを変更します

  16. 16

    Pythonクロージャ:関数内で定義された関数は、1レベル上の変数にアクセスできますか?

  17. 17

    関数内の変数にアクセスします。これは、テストデータで使用するモデルを定義します。Tensorflow

  18. 18

    bash関数でローカル配列を定義し、その関数の外部でアクセスします

  19. 19

    別の関数でグローバルに定義された変数の関数でデータセットにアクセスする

  20. 20

    そのクラスのオブジェクトが作成されたときにコンストラクターがメモリを取得する外部で関数がクリアされていますか。関数が呼び出されていません

  21. 21

    リストを含むデータ型を作成し、それらに関数を定義する

  22. 22

    自己内で定義された関数を使用してクラス変数を更新します

  23. 23

    リアクティブ関数から作成されたデータにアクセスして、ShinyでreactiveValuesを定義します

  24. 24

    関数が、定義された文字列ではなく、パラメータとしてそれ自体を含むクラスを必要とするのはなぜですか?

  25. 25

    別の関数で定義されているアクセス関数

  26. 26

    Pythonモジュールでアンダースコアのようなアクセス修飾子を使用すると、定義された変数またはインポートされたモジュール、関数、クラスにアクセスする方法が制御されますか?

  27. 27

    関数内で作成されたオブジェクトのスコープ-参照が関数の外部に保存およびアクセスされた場合、それらは有効ですか?

  28. 28

    コンストラクター関数の新しく作成されたonjectでゲッター関数を定義するにはどうすればよいですか?

  29. 29

    基本クラスで定義された関数を子クラスでオーバーライドする

ホットタグ

アーカイブ