5

相关:Python对象转换

我最近了解到 Python 允许您像这样更改实例的类:

class Robe:
    pass

class Dress:
    pass

r = Robe()
r.__class__ = Dress

我试图弄清楚是否存在这样的“变形”对象有用的情况。我在 IDLE 中搞砸了这个,我注意到的一件事是分配不同的类不会调用新类的__init__方法,尽管如果需要可以显式地完成。

几乎我能想到的每个用例都可以通过组合得到更好的服务,但我是一个编码新手,所以我知道什么。;)

4

2 回答 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__调用它的方法(如果存在这样的方法)。

于 2013-04-06T21:12:38.050 回答
2

我记得很久以前使用这种技术在识别现有对象持有什么样的数据后“升级”现有对象。它是实验性 XMPP 客户端的一部分。XMPP 使用许多简短的 XML 消息(“节”)进行通信。

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

我当然可以创建一个新类来表示已解析的消息,创建该类的新对象并从原始 XML DOM 对象复制相关数据。不过,就地更改对象的类有两个好处。首先,XMPP 是一个非常可扩展的标准,如果代码的其他部分在那里或调试时发现有用的东西,仍然可以轻松访问原始 DOM 对象是很有用的。其次,分析代码告诉我,创建一个新对象并显式复制数据比仅重用无论如何都会很快销毁的对象要慢得多——这种差异在 XMPP 中已经足够重要了,它使用许多短消息。

我认为这些原因中的任何一个都不能证明在生产代码中使用这种技术是合理的,除非你真的需要 CPython 中的(不是那么大的)加速。这只是一个 hack,我发现它有助于在实验应用程序中使代码更短、更快。还要注意,这种技术很容易破坏非 CPython 实现中的 JIT 引擎,使代码变得更慢!

于 2013-04-06T21:31:07.737 回答