web-dev-qa-db-ja.com

pythonで内部クラスから外部クラスにアクセス

私はそのような状況にあります...

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.Outer.some_method()    # <-- this is the line in question

OuterクラスからInnerクラスのメソッドにアクセスするにはどうすればよいですか?

編集-回答いただきありがとうございます。これを実装するためにどのように設計したかを再評価し、より堅牢な方法を考え出す必要があると結論しています。

78
T. Stone

ネストされたクラスのメソッドは、外部クラスのインスタンス属性に直接アクセスできません。

内部クラスのインスタンスを作成した場合でも、必ずしも外部クラスのインスタンスが存在するわけではないことに注意してください。

実際、ネストされたクラスは内部クラスと外部クラスの間の特定の関係を意味しないため、ネストされたクラスの使用は推奨されません。

51
Daniel Vassallo

内部クラスインスタンスから外部クラスインスタンスにアクセスしようとしています。そのため、factory-methodを使用して、Innerインスタンスを構築し、Outerインスタンスをそれに渡します。

class Outer(object):

    def createInner(self):
        return Outer.Inner(self)

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

        def inner_method(self):
            self.outer_instance.anothermethod()
45
Kitlbast

多分私は怒っていますが、これは確かに非常に簡単に思えます-事は外側のクラスのメソッド内にあなたの内側のクラスを作ることです...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

さらに...「自己」は慣例でのみ使用されるため、これを行うことができます。

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

外部クラスの外側からこの内部クラスを作成できないことに反対するかもしれません...しかし、これは真実ではありません:

class Bumblebee():

    def do_sthg( self ):
        print "sthg"

    def giveMeAnInnerClass( outerClassSelf ):

        class mooble():
            def do_sthg_different( self ):
                print "something diff\n"
                outerClassSelf.do_sthg()
        return mooble

それから、どこか数マイル離れたところ:

blob = Bumblebee().giveMeAnInnerClass()()
blob.do_sthg_different()    

ボートを少し押し出し、この内部クラスを拡張して(super()が機能するようにするには、moobleのクラスシグネチャを「class mooble(object)」に変更する必要があります)

class InnerBumblebeeWithAddedBounce( Bumblebee().giveMeAnInnerClass() ):
    def bounce( self ):
        print "bounce"

    def do_sthg_different( self ):
        super( InnerBumblebeeWithAddedBounce, self ).do_sthg_different()
        print "and more different"


ibwab = InnerBumblebeeWithAddedBounce()    
ibwab.bounce()
ibwab.do_sthg_different()

later

mrh1997は、この手法を使用して配信される内部クラスの非一般的な継承について興味深い点を提起しました。しかし、解決策は非常に簡単なようです:

class Fatty():
    def do_sthg( self ):
        pass

    class InnerFatty( object ):
        pass

    def giveMeAnInnerFattyClass(self):
        class ExtendedInnerFatty( Fatty.InnerFatty ):
            pass
        return ExtendedInnerFatty

fatty1 = Fatty()
fatty2 = Fatty()

innerFattyClass1 = fatty1.giveMeAnInnerFattyClass()
innerFattyClass2 = fatty2.giveMeAnInnerFattyClass()

print ( issubclass( innerFattyClass1, Fatty.InnerFatty ))
print ( issubclass( innerFattyClass2, Fatty.InnerFatty ))
24
mike rodent

このようなクラスをネストするのではなく、継承を使用するつもりですか?あなたがしていることは、Pythonでは意味がありません。

内部クラスのメソッド内でOuter.some_methodを参照するだけでOuterのsome_methodにアクセスできますが、期待どおりに機能しません。たとえば、これを試してみると:

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            Outer.some_method()

... Outer.some_methodInnerインスタンスを最初の引数として受け取ることを想定しているため、Outerオブジェクトを初期化するとTypeErrorが発生します。 (上記の例では、基本的にsome_methodOuterのクラスメソッドとして呼び出そうとしています。)

4
zenbot

私はいくつかのPythonコードを作成しましたouterクラス)をそのinnerクラスから、この質問に対する別の answer からの良いアイデアに基づいています。

_class higher_level__unknown_irrelevant_name__class:
    def __init__(self, ...args...):
        ...other code...
        # Important lines to access sub-classes.
        subclasses = self._subclass_container()
        self.some_subclass = subclasses["some_subclass"]
        del subclasses # Free up variable for other use.

    def sub_function(self, ...args...):
        ...other code...

    def _subclass_container(self):
        _parent_class = self # Create access to parent class.
        class some_subclass:
            def __init__(self):
                self._parent_class = _parent_class # Easy access from self.
                # Optional line, clears variable space, but SHOULD NOT BE USED
                # IF THERE ARE MULTIPLE SUBCLASSES as would stop their parent access.
                #  del _parent_class
        class subclass_2:
            def __init__(self):
                self._parent_class = _parent_class
        # Return reference(s) to the subclass(es).
        return {"some_subclass": some_subclass, "subclass_2": subclass_2}
_

メインコード、「プロダクション対応」(コメントなしなど)。山かっこ内の各値(たとえば_<x>_)をすべて目的の値に置き換えてください。

_class <higher_level_class>:
    def __init__(self):
        subclasses = self._subclass_container()
        self.<sub_class> = subclasses[<sub_class, type string>]
        del subclasses

    def _subclass_container(self):
        _parent_class = self
        class <sub_class>:
            def __init__(self):
                self._parent_class = _parent_class
        return {<sub_class, type string>: <sub_class>}
_

この方法の仕組みの説明(基本手順):

  1. __subclass_container_という名前の関数を作成して、(関数内で実行されているコードからの)上位クラスへの参照である変数selfにアクセスするラッパーとして機能します。

    1. __parent_class_のサブクラスがアクセスできるこの関数の変数selfへの参照である__subclass_container_という名前の変数を作成します(他のself変数との名前の競合を回避します)サブクラスで)。

    2. __subclass_container_関数を呼び出すコードが内部のサブクラスにアクセスできるように、サブクラス/サブクラスを辞書/リストとして返します。

  2. 上位クラス(または必要な場所)内の___init___関数で、関数__subclass_container_から返されたサブクラスを変数subclassesに受け取ります。

  3. subclasses変数に格納されているサブクラスを、より高いレベルのクラスの属性に割り当てます。

シナリオを簡単にするためのいくつかのヒント:

サブクラスをより高いレベルのクラスに割り当てるコードをコピーしやすくし、___init___関数が変更されました:

メインコードの行12の前に挿入します。

_    def _subclass_init(self):
_

次に、この関数に(メインコードの)行5〜6を挿入し、行4〜7を次のコードに置き換えます。

_        self._subclass_init(self)
_

サブクラスの数が多い/不明な場合に、より高いレベルのクラスにサブクラスを割り当てることができるようにします。

6行目を次のコードに置き換えます。

_        for subclass_name in list(subclasses.keys()):
            setattr(self, subclass_name, subclasses[subclass_name])
_

このソリューションが有用であり、より高いレベルのクラス名を取得できない場合のシナリオ例:

"a"(_class a:_)という名前のクラスが作成されます。それにアクセスする必要があるサブクラス(親)があります。 1つのサブクラスは「x1」と呼ばれます。このサブクラスでは、コードa.run_func()が実行されます。

次に、「b」という名前の別のクラスが作成され、クラス「a」(class b(a):)からderivedが作成されます。その後、いくつかのコードがb.x1()を実行します(派生サブクラスであるbのサブ関数「x1」を呼び出します)。この関数はa.run_func()を実行し、クラス "a"の関数 "run_func"を呼び出します。not親の関数 "run_func"クラス「a」で定義された関数は、その親であるクラス「a」の関数を参照するように設定されているためです。

これは問題を引き起こします(たとえば、関数_a.run_func_が削除された場合)、クラス_a.x1_のコードを書き換えない唯一の解決策は、サブクラス_x1_をすべての更新コードで再定義することですクラス「a」から派生したクラスは明らかに難しく、それだけの価値はありません。

3
Edward

別の可能性:

class _Outer (object):
    # Define your static methods here, e.g.
    @staticmethod
    def subclassRef ():
        return Outer

class Outer (_Outer):
    class Inner (object):
        def outer (self):
            return _Outer

        def doSomething (self):
            outer = self.outer ()
            # Call your static mehthods.
            cls = outer.subclassRef ()
            return cls ()
2
tsnorri

メタクラスを使用して外部クラスに簡単にアクセスできます:外部クラスの作成後、クラスの属性辞書をチェックし(または必要なロジックを適用します-私の例は簡単な例です)、対応する値を設定します:

import six
import inspect


# helper method from `peewee` project to add metaclass
_METACLASS_ = '_metaclass_helper_'
def with_metaclass(meta, base=object):
    return meta(_METACLASS_, (base,), {})


class OuterMeta(type):
    def __new__(mcs, name, parents, dct):
        cls = super(OuterMeta, mcs).__new__(mcs, name, parents, dct)
        for klass in dct.values():
            if inspect.isclass(klass):
                print("Setting outer of '%s' to '%s'" % (klass, cls))
                klass.outer = cls

        return cls


# @six.add_metaclass(OuterMeta) -- this is alternative to `with_metaclass`
class Outer(with_metaclass(OuterMeta)):
    def foo(self):
        return "I'm outer class!"

    class Inner(object):
        outer = None  # <-- by default it's None

        def bar(self):
            return "I'm inner class"


print(Outer.Inner.outer)
>>> <class '__main__.Outer'>
assert isinstance(Outer.Inner.outer(), Outer)

print(Outer().foo())
>>> I'm outer class!
print(Outer.Inner.outer().foo())
>>> I'm outer class!
print(Outer.Inner().outer().foo())
>>> I'm outer class!
print(Outer.Inner().bar())
>>> I'm inner class!

このアプローチを使用すると、相互に2つのクラスを簡単にバインドおよび参照できます。

2
grundic

@tsnorriの説得力のある考え方を拡張し、外側のメソッドは 静的メソッド である可能性があること

class Outer(object):

    @staticmethod
    def some_static_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.some_static_method()    # <-- this will work later

    Inner.some_static_method = some_static_method

これで、問題の行は実際に呼び出されるまでに機能するはずです。

上記のコードの最後の行は、Innerクラスに、Outer静的メソッドのクローンである静的メソッドを提供します。


これは、2つのPython機能、 関数はオブジェクト 、および スコープはテキスト という機能を利用しています。

通常、ローカルスコープは(テキストの)現在の関数のローカル名を参照します。

...または現在のclass私たちの場合。したがって、Outerクラスの定義に対して「ローカル」オブジェクト(Innerおよびsome_static_method)は、その定義内で直接参照できます。

1
Bob Stein

this が見つかりました。

あなたの質問に合わせて微調整し、それが答えです:

class Outer(object):
    def some_method(self):
        # do something

    class _Inner(object):
        def __init__(self, outer):
            outer.some_method()
    def Inner(self):
        return _Inner(self)

私はあなたが何らかの形でこれまたは何かのためにデコレータを書くことができると確信しています:)
/edit: kinda

1
flying sheep