2010-02-17 16 views
6

Rozważmy następujący Python (działa w 2.x lub 3.x)Klasy wewnętrzne: Jak uzyskać obiekt klasy zewnętrznej w czasie budowy?

class Outer(object): 
    pass 

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

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

Chcę dostać w swoje ręce o natomiast wewnątrz Inner.__init__(). Ale:

  • Nie chcę o być jawne parametrem Inner.
  • Chcę, aby O.Inner i o.Inner były obiektem klasy, a nie czymś dziwnym jak zamknięcie.

Czy możesz zasugerować, w jaki sposób mogę to osiągnąć?

Teraz moim najlepszym pomysłem jest użycie lokalnego magazynu wątków. W moim przypadku użycia, ilekroć skonstruować o.Inner(), jestem już wewnątrz metody na o gdzieś, i nie byłoby to nic wielkiego, aby dodać

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

do mojego kodu.

Daje to pojęcie o poziomie deprawacji, który chcę rozważyć.

+2

Jest to jeden z tych przypadków, w których musimy zapytać: "Co to jest, co próbujesz osiągnąć? Ponieważ jest prawdopodobnie lepszy sposób na zrobienie tego." W Javie nie można umieścić dwóch (publicznych) klas w tym samym pliku, chyba że jeden z nich znajduje się w drugim. W Pythonie nie ma takiego wymogu, więc prawdopodobnie istnieje sposób, aby zrobić to, co chcesz, bez korzystania w ogóle z klas wewnętrznych. – MatrixFrog

Odpowiedz

12

W Pythonie 2.6, klasa dekorator to również zwyczaj deskryptor spełniających specyfikacje podać:

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) 

ten emituje:

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

tylko w celu potwierdzenia, że ​​

o.Inner [[is]] klasa obiekt, nie coś dziwnego jak zamknięcie

według specyficznych specyfikacji.Oczywiście musi być inny klasa ponowne wejścia za każdym razem - nawet w jednowątkowym świecie, co następuje:

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

powinien pracować czysto i stashing o1 vs o2 nigdzie indziej niż w klasach zwrócone przez o1.Inner vs o2.Inner (np. w TLS) oznaczałoby straszne wyniki dla tego wykorzystania.

Ale wtedy nie określenie „o.Inner musi być dokładnie ta sama klasa obiektu dla każdy możliwe o to wystąpienie Outer”, więc ten kod w pełni spełnia specyfikacje ty zrobił give ;-).

+0

Cudowny! Teraz muszę medytować, czy mogę żyć z o1.Inner! = O2.Inner! = Outer.Inner. Oczywiście metody związane! = Niezwiązane metody, więc ma sens tylko to, że powiązane klasy potomne! = Niezwiązane klasy potomne. –

+0

Jak napisano, twoje podejście zwraca nową klasę za każdym razem, gdy odwołuje się o.Inner, więc "assert o.Inner == o.Inner" zawiedzie. Buforowanie klasy Wrapper w instancji, takiej jak setattr (instancja, "__cache__" + self.cls .__ name__), a następnie preferowanie wersji z pamięci podręcznej, jeśli jest dostępna, rozwiązuje co najmniej. –

+0

Kolejna poprawka: jeśli "instancja" jest Brak, to __get__ powinien zwrócić self.cls.Bez tego isinstance (i, Outer.Inner) zwraca False. –

0

Powinieneś przeprojektować swój kod, aby nie używać wewnętrznych klas i jawnie przekazywać instancję Zewnętrznego do Wewnętrznego, gdy go tworzysz, lub nie wymagać tego.

0

Nie można (w przenośny, niezawodny ani wydajny sposób). Zagnieżdżone klasy nie mają związku z klasą zagnieżdżania. Nie mają też żadnej wartości dodanej. Zamiast używać klas zagnieżdżonych, użyj przechowywania wystąpień - zdefiniuj osobno osobno definicje i utwórz instancję Inner w Outer.__init__, ewentualnie podając Outer lub instancję Outer, jeśli jest to potrzebne. Jeśli nalegasz, możesz uczynić Inner atrybutem klasy Outer, ale nie będzie on miał specjalnego dostępu do Outer.

+1

@Thomas, więc jedna z trzech cech, o których myślisz, że moja odpowiedź jest nieobecna - wydaje się dość przenośna, niezawodna, _ i efektywna dla mnie (także całkiem elegancka, jeśli powiem to sobie ;-). "Specjalny dostęp" przychodzi poprzez Inner będący klasą descriptor_custom (która owija oryginalny obiekt klasy Inner za pomocą składni dekoratora - tam właśnie pojawia się wymaganie 2.6, ponieważ musi to być oczywiście dekorator klasy) - tak, że kiedykolwiek 'o.Inner' jest dostępny, wchodzi w grę' o.Inner .__ get__' (i dostaje 'o' jako argument). –

+0

Szczerze mówiąc, nie myślałem o używaniu dekoratora klasy. Nadal wydaje się paskudnym hackerem bez żadnego przyzwoitego celu. Jeśli chodzi o to, którego z trzech nie ma, oczywiście przenośność (działa tylko w Pythonie 2.6 i później :) - chociaż chciałbym zaznaczyć, że z twoim rozwiązaniem wewnętrzna klasa nie może sama być deskryptorem. Kolejny kod w bloku klasy nie może bezpośrednio korzystać z klasy wewnętrznej, która jest prawdopodobnie częścią tego, że jest to klasa wewnętrzna. Podsumowując, wciąż odpowiadam, że nie powinieneś tworzyć takich współzależnych klas; złożoność nie jest tego warta. –

2

Nie można tego zrobić. Ale przy odrobinie przeprojektowania:

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

To jest fajne. po prostu określasz, czego potrzebuje wewnętrzna klasa i generujesz ją za pomocą funkcji z zewnętrznego obiektu. –

+0

Właściwie, myślę, że można to zrobić (chyba, że ​​opisujesz coś innego, co myślę). Zobacz moją odpowiedź na demonstrację. – Edward

3

Możesz użyć metaklasu do zaimplementowania deskryptora __get__, który wiąże klasę wewnętrzną z zewnętrzną. A ponieważ wydaje się, że jesteś zainteresowany tylko wiązaniem z klasą, rozważ modyfikowanie wewnętrznej klasy w miejscu, w przeciwieństwie do funkcji, która jest zawijana w metodę.

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

Jeśli wolisz owinąć wewnętrzną klasę poprzez podklasy następnie zastąpić __get__ organizmowi:

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

Pozwólcie mi się jeszcze trochę pobawić, ale myślę, że może to służyć moim celom lepiej niż odpowiedź Alexa Martellego, który jest wprawdzie łagodny umysłowo. Jeśli tak, zmienię swój znacznik wyboru. Dzięki! (I dla wszystkich, którzy powiedzieli: "to nie da się zrobić" - phooey!) –

+0

Daję ci czek, tak jak rozwiązałeś problem, jak stwierdzono, z kreatywnym rozwiązaniem, które pokazało mi coś nowego . Ale nie mogę użyć tego podejścia. Nieusuwalnym problemem jest to, że nie może być bezpieczny dla wielu serwerów. Jednakże, ponieważ nie było to stwierdzone wymaganie, nie mogę zarzucać ci winy za nieprzestrzeganie tego. –

+0

Przepraszam, nowy w Stack Overflow, myślałem, że możesz * dać * wiele kontroli. Zdecydowanie dam ci uprowadzenie, przepraszam za zamieszanie. –

Powiązane problemy