Wie kann man von einer inneren Klasse auf die äußere Klasse zugreifen?

99

Ich habe eine Situation wie diese ...

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

Wie kann ich von der OuterKlasse aus auf die Methode der InnerKlasse zugreifen ?

T. Stone
quelle
Warum tust du das? Was ist falsch an einfachen Peer-Beziehungen? Versuchen Sie etwas zu "verbergen"?
S.Lott
1
Ein Beispiel für dieses Szenario könnte eine Klasse mit Unterklassen sein, die wie gewohnt auf die äußere Klasse zugreifen müssen, dann aber eine andere Klasse (oberste Ebene) erstellen müssen, die von der ersten Klasse abgeleitet ist. In diesem Fall würden die Unterklassen der zweiten Klasse versuchen, auf die übergeordnete Klasse zuzugreifen self.<original_parent_name>und die ursprüngliche Klasse abzurufen, nicht die neue Klasse, aus der sie eine Unterklasse sind . Ich hoffe, dass die Leute, die dies lesen, dieses schwierige Szenario visualisieren und den Sinn solcher Fragen erkennen können.
Edward
1
Duplizieren Sie auf stackoverflow.com/questions/2278426 und es hat eine schöne Antwort.
Xmedeko

Antworten:

66

Die Methoden einer verschachtelten Klasse können nicht direkt auf die Instanzattribute der äußeren Klasse zugreifen.

Beachten Sie, dass eine Instanz der äußeren Klasse nicht unbedingt vorhanden ist, selbst wenn Sie eine Instanz der inneren Klasse erstellt haben.

Tatsächlich wird häufig empfohlen, keine verschachtelten Klassen zu verwenden, da die Verschachtelung keine bestimmte Beziehung zwischen der inneren und der äußeren Klasse impliziert.

Daniel Vassallo
quelle
1
Hmm, Python ist flotter als Java / C ++ ... siehe meine Antwort unten. Wenn wir Haare spalten, wie wir es normalerweise sind, kann ich Ihnen nicht wirklich sagen, ob meine "verschachtelte Klasse innerhalb der Methode" als innere Klasse zählt. An dieser Stelle muss ich mich jedoch auf das Tippen von Enten berufen: Wenn es alles tut, was eine innere Klasse möglicherweise tun könnte ... aus pythonischer Sicht ist es wahrscheinlich an der Zeit, sich mit spaltenden Haaren zu langweilen
Mike Nagetier
21
Eine innere Klasse impliziert natürlich eine Beziehung zur äußeren Klasse, was typischerweise mit dem impliziten Verwendungsbereich der inneren Klasse oder auf andere Weise mit einem organisatorischen Namespace zu tun hat.
Acumenus
59

Sie versuchen, von der inneren Klasseninstanz auf die Klasseninstanz von Outer zuzugreifen. Verwenden Sie also einfach die Factory-Methode, um die innere Instanz zu erstellen und die äußere Instanz an sie zu übergeben.

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()
Kitlbast
quelle
Dies ist genau die richtige Antwort - sollte als die richtige Antwort gewählt werden, da sie eine Lösung für die Frage des OP bietet - vielen Dank!
Daniel
28

Vielleicht bin ich verrückt, aber das scheint in der Tat sehr einfach zu sein - die Sache ist, deine innere Klasse in eine Methode der äußeren Klasse zu verwandeln ...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

Plus ... "self" wird nur von Konventionen verwendet, also können Sie dies tun:

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

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

Es könnte beanstandet werden, dass Sie diese innere Klasse dann nicht von außerhalb der äußeren Klasse erstellen können ... aber das ist nicht wahr:

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

dann irgendwo meilenweit entfernt:

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

Schieben Sie das Boot sogar ein wenig heraus und erweitern Sie diese innere Klasse (NB, damit super () funktioniert. Sie müssen die Klassensignatur von mooble in "class mooble (object)" ändern.

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

später

mrh1997 sprach einen interessanten Punkt über die nicht häufige Vererbung innerer Klassen an, die mit dieser Technik geliefert wurden. Aber es scheint, dass die Lösung ziemlich einfach ist:

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 ))
Mike Nagetier
quelle
1
Das funktioniert bei mir. Wie heißt dieses Konstrukt genau? Eine Fabrikfunktion? Eine Schließung?
Nakedfanatic
Ich habe keine Ahnung, wie es heißt ... aber ich könnte vorschlagen, dass der Grund, warum die anderen Poster dies nicht sahen, darin besteht, dass es vielleicht nicht vollständig gewürdigt wurde, dass die meisten Dinge in Python nicht heilig sind, einschließlich "Selbst" "(willkürlicher Name) und Klassen - sie sind" erstklassige Objekte ", was zu bedeuten scheint, dass Sie sie auf ziemlich empörende Weise manipulieren können
Mike Nagetier
Gute Arbeit. Inspiriert von dieser Lösungsidee und ein wenig Nachdenken habe ich Teile dieser Antwort erweitert, um unten eine weitere Antwort mit weiteren Erklärungen zu erstellen.
Edward
1
@mikerodent Aufgrund Ihres Kommentars (zu meiner Antwort) habe ich jetzt meine Antwort bearbeitet, um die Beschreibung des "Community-Wikis" zu entfernen. Wie Sie habe ich keine Kenntnisse über "Community-Wiki" -Antworten, daher ist es gut, dass Sie Ihren Fehler behoben haben.
Edward
@mikerodent Auch nichts für ungut, aber ich glaube nicht, dass "Community Wiki" -Antworten ein "Community Wiki" -Tag haben, sie müssen nur eine Art Antwort sein. Es wird wahrscheinlich eine Hilfeseite mit einer Beschreibung der "Community Wiki" -Antworten auf Stack Overflow geben, wenn Sie zufällig interessiert sind.
Edward
4

Wollen Sie Vererbung verwenden, anstatt Klassen wie diese zu verschachteln? Was Sie tun, macht in Python keinen Sinn.

Sie können auf die Outersome_method des 'zugreifen, indem Sie nur auf Outer.some_methoddie Methoden der inneren Klasse verweisen , aber es wird nicht so funktionieren, wie Sie es erwarten. Wenn Sie dies beispielsweise versuchen:

class Outer(object):

    def some_method(self):
        # do something

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

... erhalten Sie beim Initialisieren eines InnerObjekts einen TypeError , da Outer.some_methoderwartet wird, dass eine OuterInstanz als erstes Argument empfangen wird . (Im obigen Beispiel versuchen Sie im Grunde, some_methodals Klassenmethode von aufzurufen Outer.)

Zenbot
quelle
1
Der Grund, warum es wahrscheinlich keinen Sinn ergibt, ist, dass ich absichtlich hackig bin. Das Hinzufügen von benutzerdefinierten Methoden zu einem QuerySet in Django erfordert ein wenig Boilerplate-Code, und ich habe versucht, mithilfe von Python eine clevere Methode abzuleiten, mit der ich den Boilerplate-Code vorlegen und einfach die entsprechenden Teile in meinen Modellcode schreiben kann.
T. Stone
Entschuldigung - Ich kenne Django nicht und kann daher keine Möglichkeit vorschlagen, den Boilerplate-Code zu erstellen, aber Sie bellen möglicherweise den falschen Baum, wenn Sie versuchen, Ihre Klassen zu verschachteln. Ihre innere Klasse erwirbt nichts von Ihrer äußeren Klasse. Alles, was Sie in Outer verschachteln müssen, ist, Sie zu zwingen, über Outer.Inner darauf zuzugreifen, anstatt nur auf Inner.
Zenbot
3

Sie können einfach über die Metaklasse auf die äußere Klasse zugreifen: Nach der Erstellung der äußeren Klasse überprüfen Sie das Attribut dikt für alle Klassen (oder wenden Sie jede Logik an, die Sie benötigen - meine ist nur ein triviales Beispiel) und legen Sie die entsprechenden Werte fest:

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!

Mit diesem Ansatz können Sie problemlos zwei Klassen untereinander binden und verweisen.

grundic
quelle
3

Ich habe Python-Code erstellt, um eine äußere Klasse aus der inneren Klasse zu verwenden , basierend auf einer guten Idee aus einer anderen Antwort auf diese Frage. Ich denke, es ist kurz, einfach und leicht zu verstehen.

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}

Der Hauptcode "produktionsbereit" (ohne Kommentare usw.). Denken Sie daran, alle Werte in spitzen Klammern (z. B. <x>) durch den gewünschten Wert zu ersetzen .

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

Erläuterung der Funktionsweise dieser Methode (grundlegende Schritte):

  1. Erstellen Sie eine Funktion mit dem Namen _subclass_container, die als Wrapper für den Zugriff auf die Variable fungiert self, einen Verweis auf die übergeordnete Klasse (aus Code, der in der Funktion ausgeführt wird).

    1. Erstellen Sie eine Variable mit dem Namen, _parent_classdie auf die Variable selfdieser Funktion verweist, auf die die Unterklassen _subclass_containerzugreifen können (vermeidet Namenskonflikte mit anderen selfVariablen in Unterklassen).

    2. Geben Sie die Unterklasse / Unterklassen als Wörterbuch / Liste zurück, damit der Code, der die _subclass_containerFunktion aufruft, auf die darin enthaltenen Unterklassen zugreifen kann.

  2. __init__Empfangen Sie in der Funktion innerhalb der übergeordneten Klasse (oder wo immer dies erforderlich ist) die zurückgegebenen Unterklassen von der Funktion _subclass_containerin die Variable subclasses.

  3. Weisen Sie den subclassesAttributen der übergeordneten Klasse in der Variablen gespeicherte Unterklassen zu.

Einige Tipps zur Vereinfachung von Szenarien:

Vereinfachen des Kopierens des Codes zum Zuweisen der Unterklassen zur übergeordneten Klasse und zum Verwenden in Klassen, die von der übergeordneten Klasse abgeleitet sind und deren __init__ Funktion geändert wurde:

Fügen Sie vor Zeile 12 den Hauptcode ein:

def _subclass_init(self):

Fügen Sie dann in diese Funktion die Zeilen 5-6 (des Hauptcodes) ein und ersetzen Sie die Zeilen 4-7 durch den folgenden Code:

self._subclass_init(self)

Ermöglichen der Zuordnung von Unterklassen zur übergeordneten Klasse, wenn viele / unbekannte Mengen von Unterklassen vorhanden sind.

Ersetzen Sie Zeile 6 durch den folgenden Code:

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

Beispielszenario, wo diese Lösung nützlich wäre und wo der übergeordnete Klassenname nicht zu erhalten sein sollte:

Eine Klasse mit dem Namen "a" ( class a:) wird erstellt. Es hat Unterklassen, die darauf zugreifen müssen (das übergeordnete Element). Eine Unterklasse heißt "x1". In dieser Unterklasse wird der Code a.run_func()ausgeführt.

Dann wird eine andere Klasse mit dem Namen "b" erstellt, die von der Klasse "a" ( class b(a):) abgeleitet ist. Danach wird ein Teil des Codes ausgeführt b.x1()(Aufruf der Unterfunktion "x1" von b, einer abgeleiteten Unterklasse). Diese Funktion wird ausgeführt a.run_func()und ruft die Funktion "run_func" der Klasse "a" auf, nicht die Funktion "run_func" des übergeordneten Elements "b" (wie es sollte), da die in der Klasse "a" definierte Funktion auf referenziert gesetzt ist auf die Funktion der Klasse "a", da dies ihr Elternteil war.

Dies würde Probleme verursachen (z. B. wenn die Funktion a.run_funcgelöscht wurde) und die einzige Lösung ohne Umschreiben des Codes in der Klasse a.x1wäre die Neudefinition der Unterklasse x1mit aktualisiertem Code für alle Klassen, die von der Klasse "a" abgeleitet sind, was offensichtlich schwierig und nicht wert wäre es.

Edward
quelle
danke für deinen netten Kommentar zu meiner Antwort. Ich wusste nichts von "Community Wikis Answer" -Tags und habe diese jetzt entfernt! Vielen Dank auch für den Hinweis. Vielleicht möchten Sie Ihre Antwort entsprechend bearbeiten, um Verwirrung für zukünftige Leser zu vermeiden!
Mike Nagetier
3

Ich fand diese .

An Ihre Frage angepasst:

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)

Ich bin sicher, Sie können irgendwie einen Dekorateur dafür schreiben oder so

Verwandte: Was ist der Zweck der inneren Klassen von Python?

fliegende Schafe
quelle
2

Andere Möglichkeit:

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 ()
tsnorri
quelle
1

Erweitern Sie @ tsnorris überzeugendes Denken, dass die äußere Methode eine statische Methode sein könnte :

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

Jetzt sollte die betreffende Leitung zu dem Zeitpunkt funktionieren, zu dem sie tatsächlich aufgerufen wird.

Die letzte Zeile im obigen Code gibt der inneren Klasse eine statische Methode, die ein Klon der äußeren statischen Methode ist.


Dies nutzt zwei Python-Funktionen: Funktionen sind Objekte und der Umfang ist textuell .

Normalerweise verweist der lokale Bereich auf die lokalen Namen der (textuell) aktuellen Funktion.

... oder aktuelle Klasse in unserem Fall. Daher können Objekte, die "lokal" zur Definition der äußeren Klasse ( Innerund some_static_method) gehören, direkt in dieser Definition referenziert werden.

Bob Stein
quelle
1

Ein paar Jahre zu spät zur Party ... aber um die @mike rodentwundervolle Antwort zu erweitern, habe ich unten mein eigenes Beispiel gegeben, das zeigt, wie flexibel seine Lösung ist und warum sie akzeptiert werden sollte (oder hätte sein sollen) Antworten.

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

Ausgabe:

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

Wieder eine wundervolle Antwort, Requisiten an dich, Mike!

T-Rex
quelle
-2

Es ist zu einfach:

Eingang:

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

Ausgabe

Klasse A func1

Klasse B func1

Dhiman Ghosh
quelle
2
Hmm, beachte die Frage und die Wörter greifen auf die äußere Klasse zu . Durch einen glücklichen, ziemlich amüsanten Zufall haben sowohl Ihre äußere als auch Ihre innere Klasse eine Methode func1. Und ebenso amüsant erstellen Sie ein AInneres im Konstruktor von B, das absolut NICHT das äußere Klassenobjekt Adieses bestimmten ist B.
Mike Nagetier