在父类构造函数接收到特定字符串时,以Pythonic方式将子类实例赋值给变量

5 投票
2 回答
1885 浏览
提问于 2025-04-16 02:09

我想创建一个父类X的实例,并且希望能传入一个字符串“Q”作为额外的参数。
这个字符串是用来作为父类X的一个子类Q的标识符。
我希望父类的实例能够变成(或者被替换为)这个子类的实例。

我知道这可能是一个经典的问题(或者说是错误?)。不过经过一番搜索,我没有找到合适的解决方案。
于是我自己想出了一个解决办法;
我在父类的init方法中添加了一个字典,用来存储可能的标识符,字典的键是它们对应的基类实例。
然后把对应子类的class属性赋值给当前实例的class属性。
我要求init方法的参数不能是默认值,以防止出现无限循环。
下面是这个代码在实际中的样子:

class SpecialRule:
    """"""
    name="Special Rule"
    description="This is a Special Rule."
    def __init__(self, name=None):
        """"""
        print "SpecialInit"
        if name!=None:
            SPECIAL_RULES={
                "Fly" : FlyRule(),
                "Skirmish" : SkirmishRule()
                } #dictionary coupling names to SpecialRuleclasses
            self.__class__= SPECIAL_RULES[name].__class__

    def __str__(self):
        """"""
        return self.name

class FlyRule(SpecialRule):
    """"""
    name="Fly"
    description="Flies."
    def __init__(self):
        """"""
        print "FlyInit"+self.name
        SpecialRule.__init__(self)
    def addtocontainer(self, container):
        """this instance messes with the attributes of its containing class when added to some sort of list"""

class SkirmishRule(SpecialRule):
    """"""
    name="Skirmish"
    description="Skirmishes."
    def __init__(self):
        """"""
        SpecialRule.__init__(self)
    def addtocontainer(self, container):
        """this instance messes with the attributes of its containing class when added to some sort of list"""

test=SpecialRule("Fly")
print "evaluating resulting class"
print test.description
print test.__class__
</pre></code>

输出:

> SpecialInit FlyInitFly SpecialInit 正在评估结果类 Flies. main.FlyRule >

有没有更符合Python风格的解决方案?我的方法有没有可能出现问题?
(我是不是错了,认为在子类的.__init__中显式调用父类的.__init__(self)是一个好的编程习惯?)
我的解决方案感觉有点……不太对劲……

到目前为止的快速回顾;
感谢大家的快速回答

@ Mark Tolonen的解决方案
我一直在研究__new__-method,但是当我尝试把A、B和C在Mark Tolonen的例子中作为Z的子类时,出现了“类Z尚未定义”的错误。此外,我不确定是否可以在Z的作用域外用正常的方式实例化类A(即用变量=A()),除非你已经创建了一个子类的实例,并把类作为子类实例的一个属性来调用……这似乎不太简单。__new__非常有趣,我会再多试试,你的例子比我从Python文档中得到的更容易理解。

@ Greg Hewgill的解决方案
我尝试了静态方法的解决方案,似乎效果不错。我之前考虑过使用一个单独的函数作为工厂,但我觉得在主程序块中管理一大堆松散的构造函数代码会很麻烦,所以我很高兴能把它整合到类中。
我确实尝试了一下,看看能否把创建方法变成一个装饰过的.__call__(),但结果变得有点复杂,所以我就不再继续了。

2 个回答

3

查一下__new__这个方法。它是正确的方式来重写一个类是如何被创建的,而不是初始化的。

这里有个简单的小技巧:

class Z(object):
    class A(object):
        def name(self):
            return "I'm A!"
    class B(object):
        def name(self):
            return "I'm B!"
    class C(object):
        def name(self):
            return "I'm C!"

    D = {'A':A,'B':B,'C':C}

    def __new__(cls,t):
        return cls.D[t]()
3

我会通过一个函数来处理这个问题,这个函数可以选择不同的对象:

class SpecialRule:
    """"""
    name="Special Rule"
    description="This is a Special Rule."
    @staticmethod
    def create(name=None):
        """"""
        print "SpecialCreate"
        if name!=None:
            SPECIAL_RULES={
                "Fly" : FlyRule,
                "Skirmish" : SkirmishRule
                } #dictionary coupling names to SpecialRuleclasses
            return SPECIAL_RULES[name]()
        else:
            return SpecialRule()

我使用了 @staticmethod 这个装饰器,这样你就可以在没有对象实例的情况下直接调用 create() 方法。你可以这样调用它:

SpecialRule.create("Fly")

撰写回答