内部クラスから外部クラスにアクセスする方法は?


101

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

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ですか?


なぜあなたはこれをやっている?単純なピア関係の何が問題になっていますか?何かを「隠そう」としていますか?
S.Lott

1
このシナリオの例としては、通常どおり、外部クラスにアクセスする必要があるが、最初のクラスから派生した別のクラス(トップレベル)を作成する必要があるサブクラスを持つクラスがあるとします。その場合、2番目のクラスのサブクラスは、親クラスにアクセスして、元のクラスself.<original_parent_name>取得しようとしますが、サブクラスである新しいクラスではありませんこれを読んでいる人がこの難しいシナリオを視覚化し、このような質問のポイントを見ることができることを願っています。
エドワード

1
stackoverflow.com/questions/2278426に複製すると、いい答えが得られます。
xmedeko 2018年

回答:


67

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

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

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


1
うーん、PythonはJava / C ++よりもフリスキーです...以下の私の答えを参照してください。通常のようにヘアを分割している場合、「メソッド内のネストされたクラス」が内部クラスとしてカウントされるかどうかは本当にわかりません。ただし、この時点で、ダックタイピングを呼び出す必要があります。内部クラスが実行できるすべてのことを実行する場合、Pythonicの観点からは、髪の毛を
割くのに

21
もちろん、内部クラスは外部クラスとの関係を意味します。通常、内部クラスまたは組織の名前空間の暗黙の使用範囲と関係があります。
Acumenus 14

60

内部クラスインスタンスから外部クラスインスタンスにアクセスしようとしています。したがって、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()

これは正解です。これは、OPの質問に対する解決策を提供するため、正解として選択する必要があります。
ダニエル

28

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

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

さらに... "self"は慣例でのみ使用されるため、次のようにすることができます。

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()    

ボートを少し押し出して、この内部クラスを拡張することもできます(NBが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()

後で

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 ))

1
これでうまくいきます。この構成は正確に何と呼ばれていますか?ファクトリー機能?閉鎖?
Nakedfanatic

私はそれが何と呼ばれるのかわかりません...しかし、他のポスターがこれを見なかった理由は、Pythonのほとんどのものが非神聖であることが十分に理解されていなかったためだと思いますか。 "(任意の名前)とクラス-これらは「ファーストクラスオブジェクト」です。つまり、非常に法外な方法でそれらを操作できることを意味するようです
マイク齧歯類

すごい仕事。このソリューションのアイデアと少しの考えに触発されて、私はこの回答の一部を拡張して、さらに説明を加えて以下の別の回答を作成しました。
エドワード

1
@mikerodent(私の回答に対する)あなたのコメントのため、「コミュニティウィキ」の説明を削除するために私の回答を編集しました。あなたと同じように、「コミュニティウィキ」の回答について私は何も知りません。
エドワード

@mikerodentまた、違反はありませんが、「コミュニティウィキ」の回答に「コミュニティウィキ」タグが付いているとは思いません。これらは単なる回答のタイプでなければなりません。あなたがたまたま興味を持っていたら、おそらくスタックオーバーフローに「コミュニティウィキ」の回答の説明を含むヘルプページがあるでしょう。
エドワード

4

このようにクラスをネストするのではなく、継承を使用するつもりですか?あなたがやっていることは、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()

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


1
それがおそらく意味をなさない理由は、私が故意にハックしているからです。DjangoのQuerySetにカスタムメソッドを追加するには、ボイラープレートコードが少し必要です。ボイラープレートコードをテンプレート化し、適切なパーツをモデルコードに書き込むことができるpythonを使用して、それを行うための巧妙な方法を導き出そうとしていました。
T.ストーン

お詫び-私はDjangoを知らないので、ボイラープレートコードをテンプレート化する方法を提案することはできませんが、クラスをネストしようとすると、間違ったツリーが表示される可能性があります。内部クラスは外部クラスから何も取得しません。それをOuter内にネストする場合は、プレーンなInnerではなく、Outer.Innerを介してアクセスする必要があります。
zenbot

3

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

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つのクラスを簡単にバインドして相互に参照できます。


3

私が使用するいくつかのPythonコードを作成した外部クラスをそのから内部クラス別の良いアイデアをもとに、答えこの質問について。短くてシンプルでわかりやすいと思います。

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あるvariableにアクセスするためのラッパーとして機能するという名前の関数を作成しますself

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

    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()が実行されます。

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

これは問題を引き起こし(関数a.run_funcが削除された場合など)、クラスのコードを書き直さない唯一の解決策a.x1は、x1クラス "a"から派生したすべてのクラスの更新されたコードでサブクラスを再定義することです。それ。


私の答えに対する素敵なコメントをありがとう。「コミュニティウィキの回答」タグは何も知りませんでした。これを削除しました。これを指摘してくれてありがとう。今後の読者の混乱を防ぐために、それに応じて回答を編集することをお勧めします。
マイクげっ歯類2016

3

私はこれを見つけまし

あなたの質問に合わせて微調整:

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)

きっとなんとなくこれのデコレータを書けると思います

関連:Pythonの内部クラスの目的は何ですか?


2

別の可能性:

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 ()

1

@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機能を利用しています

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

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


1

後半相手に数年....はなく、上の展開する@mike rodentのは素晴らしい答えを、私はどのように柔軟な彼の解決策があることを示しているの下に私自身の例を提供し、それがあるべき(またはすべき理由ましていれて)受け入れ回答。

Python 3.7

class Parent():

    def __init__(self, name):
        self.name = name
        self.children = []

    class Inner(object):
        pass

    def Child(self, name):
        parent = self
        class Child(Parent.Inner):
            def __init__(self, name):
                self.name = name
                self.parent = parent
                parent.children.append(self)
        return Child(name)



parent = Parent('Bar')

child1 = parent.Child('Foo')
child2 = parent.Child('World')

print(
    # Getting its first childs name
    child1.name, # From itself
    parent.children[0].name, # From its parent
    # Also works with the second child
    child2.name,
    parent.children[1].name,
    # Go nuts if you want
    child2.parent.children[0].name,
    child1.parent.children[1].name
)

print(
    # Getting the parents name
    parent.name, # From itself
    child1.parent.name, # From its children
    child2.parent.name,
    # Go nuts again if you want
    parent.children[0].parent.name,
    parent.children[1].parent.name,
    # Or insane
    child2.parent.children[0].parent.children[1].parent.name,
    child1.parent.children[1].parent.children[0].parent.name
)


# Second parent? No problem
parent2 = Parent('John')
child3 = parent2.Child('Doe')
child4 = parent2.Child('Appleseed')

print(
    child3.name, parent2.children[0].name,
    child4.name, parent2.children[1].name,
    parent2.name # ....
)

出力:

Foo Foo World World Foo World
Bar Bar Bar Bar Bar Bar Bar
Doe Doe Appleseed Appleseed John

繰り返しになりますが、素晴らしい答えです。あなたにマイクの小道具を!


-2

単純すぎる:

入力:

class A:
    def __init__(self):
        pass

    def func1(self):
        print('class A func1')

    class B:
        def __init__(self):
            a1 = A()
            a1.func1()

        def func1(self):
            print('class B func1')

b = A.B()
b.func1()

出力

クラスA func1

クラスB func1


2
うーん、質問に注意してください、そして言葉は外部クラスにアクセスします。幸せで、とても面白い、偶然の一致で、あなたの外側と内側のクラスは両方ともメソッドを持っていfunc1ます。同様に面白いのは、Aのコンストラクタの内部を作成することですB。これはA、特定のの外部クラスオブジェクトではありませんB
マイクげっ歯類
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.