2010-02-17 3 views
6

Betrachten Sie den folgenden Python (läuft in 2.x oder 3.x):Innere Klassen: Wie kann ich das Objekt der äußeren Klasse zur Konstruktionszeit erhalten?

class Outer(object): 
    pass 

    class Inner(object): 
    def __init__(self): 
     print("Inner.self", self) 

o = Outer() 
i = o.Inner() 

Ich mag, während im Innern Inner.__init__() auf o meine Hände zu bekommen. Aber:

  • Ich will nicht o ein expliziter Parameter auf Inner sein.
  • Ich möchte O.Inner und o.Inner ein Klassenobjekt sein, nicht etwas Seltsames wie eine Schließung.

Können Sie mir vorschlagen, wie ich das erreichen könnte?

Im Moment ist meine beste Idee, lokalen Threadspeicher zu verwenden. In meinem Anwendungsfall, wenn ich ein o.Inner() konstruieren, ich bin schon in einem Verfahren auf o irgendwo, und es wäre keine große Sache

threading.local()["my o object"] = o 

zu meinem Code hinzuzufügen sein.

Dies gibt Ihnen eine Vorstellung von dem Grad der Verdorbenheit, die ich bereit bin zu berücksichtigen.

+2

Dies ist einer dieser Fälle, in denen wir fragen müssen: "Was willst du erreichen? Weil es wahrscheinlich einen besseren Weg gibt, es zu tun." In Java können Sie nicht zwei (öffentliche) Klassen in dieselbe Datei einfügen, es sei denn, eine davon ist innerhalb der anderen vorhanden. In Python gibt es keine solche Anforderung, also gibt es wahrscheinlich eine Möglichkeit, das zu tun, was Sie wollen, ohne überhaupt innere Klassen zu verwenden. – MatrixFrog

Antwort

12

In Python 2.6, einer Klasse Dekorateur, das ist auch eine benutzerdefinierte Deskriptor passt die Spezifikationen Sie geben:

class InnerClassDescriptor(object): 
    def __init__(self, cls): 
    self.cls = cls 
    def __get__(self, instance, outerclass): 
    class Wrapper(self.cls): 
     outer = instance 
    Wrapper.__name__ = self.cls.__name__ 
    return Wrapper 

class Outer(object): 
    @InnerClassDescriptor 
    class Inner(object): 
    def __init__(self): 
     print self.outer 

o = Outer() 
i = o.Inner() 
print 'Outer is a', type(Outer) 
print 'Inner is a', type(o.Inner) 

Dieses aussendet:

<__main__.Outer object at 0x82f90> 
Outer is a <type 'type'> 
Inner is a <type 'type'> 

nur um das zu bestätigen

o.Inner ist [[ist]] eine Klasse Objekt, nicht etwas komisch wie ein Verschluss

nach Ihren besonderen Angaben.die folgende sogar in einer Welt, single-threaded, - Natürlich braucht es eine verschiedene Klasse jedes Mal für reentrancy sein:

o1 = Outer() 
o2 = Outer() 
i1 = o1.Inner 
i2 = o2.Inner 
print i1(), i2(), i1(), i2() 

arbeiten sauber sollte, und stashing o1 vs o2 anderswo als in den Klassen kehrt von o1.Inner vs o2.Inner (zB in TLS) würde schreckliche Ergebnisse für diese Verwendung bedeuten.

Aber dann Sie hat nicht angeben „o.Inner hat genau die gleiche Klasse sein Objekt für jeder möglich o, dass eine Instanz von Outer ist“, so dass dieser Code erfüllt die Spezifikationen Sie tat give ;-).

+0

Wunderbar! Jetzt muss ich meditieren, ob ich mit o1.Inner! = O2.Inner! = Outer.Inner leben kann. Natürlich, gebundene Methoden! = Ungebundene Methoden, so macht es nur Sinn, dass gebundene Kindklassen! = Ungebundene Kindklassen. –

+0

Als Ihr Ansatz geschrieben gibt eine neue Klasse jedes Mal o.Inner Bezug genommen wird, so würde „o.Inner == o.Inner behaupten“ scheitern. Caching die Wrapper-Klasse in der Instanz, wie setattr (Beispiel: "__cache__" + self.cls .__ name__), dann lieber die im Cache gespeicherte Version, wo verfügbar, dass zumindest löst. –

+0

Eine weitere Optimierung: Wenn "instance" None ist, sollte __get__ self.cls zurückgeben.Ohne diese Angabe gibt zBInstance (i, Outer.Inner) False zurück. –

0

Sie sollten Ihren Code so umgestalten, dass keine inneren Klassen verwendet werden und die Instanz von Outer nach Inner explizit übergeben wird, wenn Sie sie erstellen oder nicht.

0

Sie können nicht (in irgendeiner tragbaren, zuverlässigen oder effizienten Weise). Geschachtelte Klassen haben keine Beziehung zu der Schachtelungsklasse. Sie haben auch keinen Mehrwert. Verwenden Sie nicht geschachtelte Klassen, sondern verwenden Sie die Eingrenzung von Instanzen - definieren Sie Outer und Inner separat und erstellen Sie eine Instanz von Inner in , möglicherweise Outer oder die Outer Instanz, wenn Sie es benötigen. Sie können Inner ein Klassenattribut von Outer machen, wenn Sie darauf bestehen, aber es wird keinen speziellen Zugriff auf Outer haben.

+1

@Thomas, so das eines der drei Merkmale denken Sie meine Antwort fehlt - scheint ziemlich kompakt, zuverlässig _and_ effizient zu mir (auch recht elegant, wenn ich so sagen mir ;-). Die „special access“ kommt durch die Innere eine _custom descriptor_ Klasse zu sein (die das ursprüngliche innere Klassenobjekt über Dekorateur Syntax Wraps - das ist, wo die 2.6 Anforderung kommt, da es ein _class_ Dekorateur natürlich sein muss) - so dass, wann immer "o.Inner" wird aufgerufen, "o.Inner .__ get__" kommt ins Spiel (und erhält "o" als Argument). –

+0

Um ehrlich zu sein, habe ich nicht an einen Klassenausstatter gedacht. Es scheint immer noch wie ein hässlicher Hack für keinen anständigen Zweck. Wie für die drei fehlt es, offensichtlich Portabilität (es funktioniert nur in Python 2.6 und höher :) - obwohl ich darauf hinweisen würde, dass mit Ihrer Lösung die innere Klasse selbst kein Deskriptor sein kann. Und der nachfolgende Code im Klassenblock kann die innere Klasse nicht direkt verwenden, was vermutlich Teil des Grundes dafür ist, dass es sich um eine innere Klasse handelt. Alles in allem halte ich immer noch an meiner Antwort fest, dass man keine voneinander abhängigen Klassen wie diese schaffen sollte; Die Komplexität ist es nicht wert. –

2

Kann nicht gemacht werden. Aber mit ein bisschen Redesign:

class Outer(object): 
    pass 

    class _Inner(object): 
    def __init__(self, outobj): 
     self.outobj = outobj 

    def Inner(self): 
    return self._Inner(self) 

o = Outer() 
i = o.Inner() 

print o, i.outobj 
+0

das ist cool. Sie geben einfach an, was die innere Klasse benötigt, und Sie generieren es über die Funktion des äußeren Objekts. –

+0

Eigentlich denke ich kann getan werden (es sei denn, Sie beschreiben etwas anderes, was ich denke). Siehe meine Antwort für eine Demonstration. – Edward

3

Sie können eine Metaklasse verwenden, um einen __get__ Deskriptor zu implementieren, der die innere Klasse an die äußere Klasse bindet. Und da Sie nur an einer Bindung an eine Klasse interessiert zu sein scheinen, sollten Sie in Betracht ziehen, die innere Klasse an Ort und Stelle zu ändern, im Gegensatz zu einer Funktion, die in eine Methode eingebettet ist.

>>> class Outer(object): 
    class Inner(object): 
     class __metaclass__(type): 
      def __get__(self, instance, owner): 
       self.owner = owner 
       return self 


>>> Outer.Inner is Outer().Inner 
True 
>>> Outer.Inner.owner is Outer 
True 

Wenn Sie eher die innere Klasse über eine Unterklasse wickeln würde dann ersetzen Sie den __get__ Körper mit:

return type(self.__name__, (self,), {'owner': owner}) 
+0

Lassen Sie mich ein bisschen mehr damit spielen, aber ich denke, dass dies meinen Zwecken besser dienen kann als Alex Martellis zugegebenermaßen geistig-schmelzend-kühle Antwort. Wenn ja, wechsle ich mein Häkchen. Vielen Dank! (Und an alle von Ihnen, die sagten "es kann nicht getan werden" - phoey!) –

+0

Ich gebe Ihnen einen Scheck, wie Sie das Problem gelöst haben, wie gesagt, mit einer kreativen Lösung, die mir etwas Neues zeigte . Aber ich kann diesen Ansatz nicht verwenden. Das unlösbare Problem ist, dass es nicht Multithread-sicher sein kann. Da dies jedoch keine festgelegte Anforderung war, kann ich Ihnen nicht vorwerfen, dass Sie es nicht behandelt haben. –

+0

Sorry, neu zu Stack Overflow, ich dachte, du könntest * mehrere Schecks geben. Ich werde dir auf jeden Fall ein Dankeschön geben, sorry für die Verwirrung. –

Verwandte Themen