如何在__init__()中用不同对象替换实例?

29 投票
3 回答
19308 浏览
提问于 2025-04-16 01:01

我在ClassA里调用一个构造函数,想要在满足某个条件时,得到的对象是另一个类(ClassB)的实例。我试着在__init__()里替换第一个参数(下面例子中的'self'),但是这样做似乎没有达到我想要的效果。

在主程序中:

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

3 个回答

17

不要搞错构造函数的用途:应该使用工厂函数。调用一个类的构造函数却返回了另一个类的实例,这样会让人很困惑。

21

我建议你使用工厂模式来解决这个问题。举个例子:

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

你需要用到 __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 技巧了。)你可以查看文档了解更多。

不过在这种情况下,我觉得用工厂函数更合适,像这样:

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

顺便提一下,如果你像我上面那样使用 __new__(),如果返回的是 ClassB,那么你在 ClassA 中写的 __init__() 方法是不会被调用的!当你在 ClassA.__new__() 中构造 ClassB 的实例时,它自己的 __init__() 方法(ClassB.__init__())会在那时被执行,所以可以理解为什么 Python 不会在同一个对象上再运行一个无关的 __init__() 方法。不过这可能会让人有点困惑,这也是支持使用工厂函数的一种理由。

撰写回答