2010-07-09 9 views
13

Llamo a un constructor en ClassA y quiero que el objeto resultante sea de una clase diferente (ClassB) si se cumple una determinada condición. He intentado reemplazar el primer argumento por __init __() ('self' en el ejemplo a continuación) dentro de __init __() pero no parece hacer lo que quiero.¿Cómo reemplazar una instancia en __init __() con un objeto diferente?

en el principal:

import ClassA 

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

en claseA/__ 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 

en ClassB/__ init__.py:

class ClassB: 
    pass 

Respuesta

27

Usted necesita __new__() para eso. (Y también es necesario para que sea una clase de nuevo estilo, asumiendo que usted está utilizando Python 2, subclasificando 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__() se ejecuta como parte del proceso de creación de instancias de clases antes de __init__(). Básicamente, __new__() es lo que realmente crea la nueva instancia, y luego se llama a __init__() para inicializar sus propiedades. Es por eso que puede usar __new__() pero no __init__() para modificar el tipo de objeto creado: una vez que se inicia __init__(), el objeto ya se ha creado y es demasiado tarde para cambiar su tipo. (Bueno ... en realidad no, pero eso se mete en la magia negra de Python muy arcana.) Ver the documentation.

En este caso, sin embargo, yo diría que una función de fábrica es más apropiado, algo así como

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

Por cierto, tenga en cuenta que si lo hace lo que hice con __new__() anterior, si una es ClassB devuelto, se llamará al método __init__() de la instancia ClassBno! Python solo llama a __init__() si el objeto devuelto desde __new__() es una instancia de la clase en la que está contenido el método __new__() (aquí ClassA). Ese es otro argumento a favor del enfoque de función de fábrica.

+0

Bien, ahora, ¿qué ocurre si quiero cambiar el objeto por uno nuevo dentro de un método de objeto, en lugar de en el constructor? Quiero un objeto que se convierta en otros objetos durante su ciclo de vida. – sneak

+0

@sneak, no se puede: el método obtiene 'self' como argumento y, por lo tanto, cualquiera que sea que se vuelvan a vincular a dicho nombre simple, no tendrá ningún efecto fuera del método. (Por supuesto, puede volver a enlazar ** nombres ** calificados como 'self .__ class__' - los nombres calificados y los nombres desnudos son totalmente diferentes en casi todos los aspectos que se le ocurran). –

+1

@Alex, el hecho de que pueda asignar 'self .__ class__' es un accidente de implementación en CPython; la especificación oficial de Python dice que esto * no debería * funcionar. Incluso si no fue un accidente, es algo bastante horrible de hacer. En resumen: por favor no lo haga, y especialmente no lo sugiera a otras personas. – habnabit

11

No intente pervertir el propósito de los constructores: use una función de fábrica. Llamar a un constructor para una clase y devolver una instancia de una clase diferente es una forma segura de causar confusión.

+5

Creo que puedo mantenerme sin confusiones, gracias. – sneak

13

Sugeriría utilizar un patrón de fábrica para esto. Por ejemplo:

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

Esta es la forma "correcta" de hacerlo, en mi humilde opinión. – ThatAintWorking

Cuestiones relacionadas