web-dev-qa-db-ja.com

ネストされたクラスのスコープ?

Pythonのネストされたクラスのスコープを理解しようとしています。コードの例を次に示します。

class OuterClass:
    outer_var = 1
    class InnerClass:
        inner_var = outer_var

クラスの作成が完了せず、エラーが表示されます。

<type 'exceptions.NameError'>: name 'outer_var' is not defined

inner_var = Outerclass.outer_varを試しても機能しません。私は得る:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

InnerClassから静的outer_varにアクセスしようとしています。

これを行う方法はありますか?

113
user214870
class Outer(object):
    outer_var = 1

    class Inner(object):
        @property
        def inner_var(self):
            return Outer.outer_var

これは、他の言語で同様のことが機能するのとまったく同じではなく、outer_varへのアクセスをスコープする代わりにグローバルルックアップを使用します。 (名前Outerがバインドされているオブジェクトを変更すると、このコードは次に実行されるときにそのオブジェクトを使用します。)

outer_varは実際にはインスタンス属性であるため、代わりにすべてのInnerオブジェクトにOuterへの参照を持たせたい場合:

class Outer(object):
    def __init__(self):
        self.outer_var = 1

    def get_inner(self):
        return self.Inner(self)
        # "self.Inner" is because Inner is a class attribute of this class
        # "Outer.Inner" would also work, or move Inner to global scope
        # and then just use "Inner"

    class Inner(object):
        def __init__(self, outer):
            self.outer = outer

        @property
        def inner_var(self):
            return self.outer.outer_var

Pythonでクラスをネストすることはやや一般的ではなく、クラス間の特別な関係が自動的に暗示されるわけではないことに注意してください。ネストしない方がいいでしょう。 (必要に応じて、Outerのクラス属性をInnerに設定することもできます。)

104
Roger Pate

私はあなたが簡単にできると思う:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

あなたが遭遇した問題はこれによるものです:

ブロックは、1つの単位として実行されるPythonプログラムテキストです。ブロックは次のとおりです。モジュール、関数本体、およびクラス定義。
(...)
スコープは、ブロック内の名前の可視性を定義します。
(...)
クラスブロックで定義された名前の範囲は、クラスブロックに制限されています。メソッドのコードブロックには拡張されません。関数スコープを使用して実装されるため、これにはジェネレーター式が含まれます。これは、以下が失敗することを意味します。

   class A:  

       a = 42  

       b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

上記の意味:
関数本体はコードブロックであり、メソッドは関数です。クラス定義に存在する関数本体から定義された名前は、関数本体に拡張されません。

あなたの場合、これを言い換えます:
クラス定義はコードブロックであり、外部クラス定義に存在する内部クラス定義から定義された名前は、内部クラス定義に拡張されません。

44
eyquem

入れ子になったクラスを使用しない場合は、より良い場合があります。ネストする必要がある場合は、これを試してください:

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

または、ネストする前に両方のクラスを宣言します。

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(この後、必要であればdel InnerClassできます。)

20
Jason Orendorff

最も簡単なソリューション:

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

それはあなたが明示的であることを必要としますが、多くの努力をしません。

4
Cristian Garcia

Pythonでは、可変オブジェクトが参照として渡されるため、外部クラスの参照を内部クラスに渡すことができます。

class OuterClass:
    def __init__(self):
        self.outer_var = 1
        self.inner_class = OuterClass.InnerClass(self)
        print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)

    class InnerClass:
        def __init__(self, outer_class):
            self.outer_class = outer_class
            self.inner_var = 2
            print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)
1
T. Alpers