Как заменить экземпляр в __init __() на другой объект?

Я вызываю конструктор в ClassA и хочу, чтобы результирующий объект был другого класса (ClassB), если выполнено определенное условие. Я попытался заменить первый аргумент на __init __() ( "self" в примере ниже) в __init __(), но он, похоже, не делает того, что я хочу.

в основном:

import ClassA

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

в 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

в ClassB/__ init __. py:

class ClassB:
    pass

Ответы

Ответ 1

Для этого вам нужно __new__(). (И вам также нужно сделать его классом нового стиля, предполагая, что вы используете Python 2, путем подкласса 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__() выполняется как часть процесса создания класса до __init__(). В основном __new__() - это то, что на самом деле создает новый экземпляр, а затем __init__() вызывается для инициализации его свойств. Вот почему вы можете использовать __new__(), но не __init__() для изменения типа созданного объекта: как только __init__() запускается, объект уже создан и слишком поздно, чтобы изменить его тип. (Ну... не совсем, но это попадает в очень загадочную черную магию Python.) См. документацию.

В этом случае, однако, я бы сказал, что функция factory более подходит, что-то вроде

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

Кстати, обратите внимание, что если вы сделаете то, что я сделал с __new__() выше, если возвращается ClassB, метод __init__() экземпляра ClassB не будет вызываться! Python только вызывает __init__(), если объект, возвращаемый из __new__(), является экземпляром класса, в котором содержится метод __new__() (здесь ClassA). Этот еще один аргумент в пользу функционального подхода factory.

Ответ 2

Я бы предложил использовать factory шаблон для этого. Например:

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)

Ответ 3

Не пытайтесь извратить назначение конструкторов: используйте функцию factory. Вызов конструктора для одного класса и возврат экземпляра другого класса - верный способ вызвать путаницу.