Python:更改实例类是否有用例?

5

相关链接:Python对象转换

我最近学到Python可以这样更改一个实例的类:

class Robe:
    pass

class Dress:
    pass

r = Robe()
r.__class__ = Dress

我正在尝试弄清楚是否有一种情况可以有用地对此对象进行“转换”。我在IDLE中试验了一下,发现将其分配给另一个类并不会调用新类的__init__方法,尽管如果需要可以显式调用。

我几乎可以想到的每个用例都可以通过组合更好地实现,但我是一个编码新手,我知道什么呢。 ;)

2个回答

6

对于像您的示例中的RobeDress这样的不相关类,很少有好的理由这样做。如果没有一些工作,很难确保最终得到的对象处于一个合理的状态。

然而,在从基类继承时,如果您想使用非标准的工厂函数或构造函数来构建基对象,则可以很有用。以下是一个例子:

class Base(object):
    pass

def base_factory():
    return Base()  # in real code, this would probably be something opaque

def Derived(Base):
    def __new__(cls):
        self = base_factory()     # get an instance of Base
        self.__class__ = Derived  # and turn it into an instance of Derived
        return self

在这个例子中,派生类的__new__方法想要使用base_factory方法构造其对象,该方法返回Base类的实例。通常这种工厂方法位于某个库中,你无法确定它是如何创建对象的(你不能只调用Base()或者super(Derived, cls).__new__(cls)来得到相同的结果)。
实例的__class__属性被重写,以便调用Derived.__new__的结果将是Derived类的一个实例,这确保如果存在Derived.__init__方法,它将被调用。

这个答案看起来非常详细,但我是编程新手,因此很难理解它。忽略__new__/构造函数方面,我可以看到如果你在某个库中定义了一个类,并且你创建了自己的类继承它,那么你可以通过创建前者的实例然后进行“转换”来创建后者的实例。然而,为什么不直接实例化继承的类呢?我承认我不理解构造函数的概念或__new__方法的功能。 - henrebotha
1
@henrebotha:我建议阅读关于Python data model的相关资料。一个类的__new__方法被调用以构造其实例。如果存在,则调用Derived.__new__将触发对Derived()的调用。通常情况下,__new__会通过super调用返回由object.__new__创建的新对象,但它并不一定非得这样做。在上面的示例中,我们从工厂函数中获取了实例,然后进行了自定义。还可以参见此前的问题 - Blckknght

2

我记得很久以前使用过这种技术,来“升级”已有的对象,以识别它们持有的数据类型。这是一个实验性质的XMPP客户端的一部分。XMPP使用许多短的XML消息(“stanza”)进行通信。

应用程序接收到一个stanza时,它会被解析成DOM树。然后,应用程序需要识别它是什么类型的stanza(存在stanza、消息stanza、自动查询等)。例如,如果它被识别为消息stanza,则DOM对象将被“升级”为提供了“get_author”、“get_body”等方法的子类。

当然,我可以只创建一个表示已解析消息的新类,创建该类的新对象,并从原始XML DOM对象中复制相关数据。然而,在原地更改对象的类有两个好处。首先,XMPP是一个非常可扩展的标准,有用的原始DOM对象仍然可以轻松访问,以防代码的其他部分在那里发现了有用的东西,或者在调试时发现有用的东西。其次,对代码进行性能分析告诉我,创建一个新对象并显式复制数据比仅重用即将快速销毁的对象要慢得多-在使用许多短消息的XMPP中这种差异足以影响性能。

我认为除非您真的需要CPython中(不是那么大的)加速,否则这些原因都不能证明在生产代码中使用此技术。这只是一个技巧,我发现在实验性应用程序中可以使代码更短、更快。还要注意,这种技术容易破坏非CPython实现中的JIT引擎,从而使代码变得很慢!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接