2010-07-09 12 views
13

Wywołuję konstruktora w ClassA i chcę, aby wynikowy obiekt był innej klasy (ClassB), jeśli pewien warunek jest spełniony. Próbowałem zastąpić pierwszy argument do __init __() ("self" w poniższym przykładzie) wewnątrz __init __(), ale nie wydaje się, aby zrobić to, co chcę.Jak zamienić instancję w __init __() na inny obiekt?

w głównym:

import ClassA 

my_obj = ClassA.ClassA(500) 
# unfortunately, my_obj is a ClassA, but I want a ClassB! 

w ClassA/__ init__.py:

import ClassB 

class ClassA: 
    def __init__(self,theirnumber): 
     if(theirnumber > 10): 
      # all big numbers should be ClassB objects: 
      self = ClassB.ClassB(theirnumber) 
      return 
     else: 
      # numbers under 10 are ok in ClassA. 
      return 

w ClassB/__ init__.py:

class ClassB: 
    pass 

Odpowiedz

27

Trzeba __new__() za to. (I trzeba także sprawiają, że klasy w nowym stylu, przy założeniu, że używasz Python 2, przez instacji object.)

class ClassA(object): 
    def __new__(cls,theirnumber): 
     if theirnumber > 10: 
      # all big numbers should be ClassB objects: 
      return ClassB.ClassB(theirnumber) 
     else: 
      # numbers under 10 are ok in ClassA. 
      return super(ClassA, cls).__new__(cls, theirnumber) 

__new__() działa jako część procesu klasy konkretyzacji przed __init__(). Zasadniczo to __new__() jest tym, co faktycznie tworzy nową instancję, a następnie wywoływana jest __init__() w celu zainicjowania jej właściwości. Dlatego możesz użyć __new__(), ale nie __init__(), aby zmienić typ utworzonego obiektu: po rozpoczęciu __init__() obiekt został już utworzony i jest za późno, aby zmienić jego typ. (Cóż ... nie do końca, ale to wchodzi w bardzo tajemniczą czarną magię Python.) Zobacz: the documentation.

W tym przypadku jednak, powiedziałbym funkcją fabrycznym jest bardziej właściwe, coś

def thingy(theirnumber): 
    if theirnumber > 10: 
     return ClassB.ClassB(theirnumber) 
    else: 
     return ClassA.ClassA(theirnumber) 

Przy okazji należy pamiętać, że jeśli robisz to, co zrobiłem z __new__() powyżej, jeżeli jest ClassB zwrócony, metoda __init__() instancji ClassB nie będzie wywoływana jako , a nie! Python wywołuje tylko __init__(), jeśli obiekt zwrócony z __new__() jest instancją klasy, w której znajduje się metoda __new__() (tutaj ClassA). To kolejny argument przemawiający za podejściem do funkcji fabryki.

+0

Ok, co teraz, jeśli chcę zamienić obiekt na nowy w metodzie obiektu, zamiast w konstruktorze? Chcę obiekt, który zamienia się w inne obiekty podczas swojego cyklu życia. – sneak

+0

@sneak, nie możesz: metoda dostaje 'self' jako argument, więc cokolwiek mogą ponownie powiązać z nazwiskiem, nie będzie miało żadnego efektu poza tą metodą. (Możesz oczywiście ponownie powiązać ** kwalifikowane ** nazwy, takie jak 'self .__ class__' - kwalifikowane nazwy i paseczki są całkowicie różne pod każdym względem, o jakim myślisz). –

+1

@Alex, fakt, że można przypisać do 'self .__ class__' jest przypadkiem wdrożenia w CPython; oficjalna specyfikacja Pythona mówi, że to * nie powinno * działać. Nawet jeśli to nie był wypadek, to strasznie. W skrócie: proszę, nie, a szczególnie proszę, nie sugeruj tego innym osobom. – habnabit

11

Nie próbuj przekręcać celu konstruktorów: użyj funkcji fabrycznej. Wywołanie konstruktora dla jednej klasy i zwrócenie instancji innej klasy jest pewnym sposobem wywołania zamieszania.

+5

Myślę, że uda mi się pozostać nieskrępowany, thx. – sneak

13

Proponuję użyć do tego wzoru fabrycznego . Na przykład:

def get_my_inst(the_number): 
    if the_number > 10: 
     return ClassB(the_number) 
    else: 
     return ClassA(the_number) 

class_b_inst = get_my_inst(500) 
class_a_inst = get_my_inst(5) 
+1

To jest "właściwy" sposób, IMHO. – ThatAintWorking

Powiązane problemy