把自己放在别的地方有多危险?

2024-03-29 06:24:24 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个类,它有一些子类。

我可以实例化类。然后我可以将其__class__属性设置为其中一个子类。在一个活动对象上,我有效地将类类型更改为其子类的类型。我可以在它上调用调用那些方法的子类版本的方法。

那么,这么做有多危险?看起来很奇怪,但是做这样的事是不是有错?尽管可以在运行时更改类型,但这是否是语言中应该完全避免的特性?为什么或者为什么不?

(根据回答,我将发布一个关于我想做什么以及是否有更好的选择的更具体的问题)。


Tags: 对象实例方法版本语言类型属性特性
3条回答

在任意类上,这极不可能工作,而且即使工作也非常脆弱。这基本上与从一个类的方法中提取底层函数对象,并在不是原始类实例的对象上调用它们是一样的。这是否有效取决于内部实现细节,并且是一种非常紧密耦合的形式。

也就是说,将对象的__class__在一组经过特殊设计的类中进行更改,以便以这种方式使用,这是非常好的。我知道你可以这么做很长一段时间,但是我还没有找到一个更好的解决方案没有同时出现在脑海中的技术的用途。所以如果你认为你有一个用例,就去做吧。只需在你的评论/文档中清楚地说明发生了什么。尤其是,这意味着所有相关类的实现都必须尊重它们的不变量/假设等,而不是能够孤立地考虑每个类,所以您需要确保任何处理相关代码的人都知道这一点!

如果您有一个长时间运行的应用程序,并且您需要用同一类的新版本替换某个对象的旧版本而不丢失数据(例如,在某些reload(mymodule)之后并且不重新加载未更改的模块),则分配__class__属性非常有用。另一个例子是如果您实现持久性-类似于pickle.load

不鼓励使用所有其他用法,特别是如果您可以在启动应用程序之前编写完整的代码。

下面是我能想到的使这件事变得危险的事情的清单,大致顺序是从最坏到最坏:

  • 这可能会让阅读或调试代码的人感到困惑。
  • 您不会得到正确的__init__方法,因此您可能不会正确初始化所有实例变量(甚至根本不会)。
  • 2.x和3.x之间的差异非常显著,可能会导致移植疼痛。
  • 类方法、手工编码的描述符、方法解析顺序的钩子等等都有一些边缘情况,它们在经典类和新样式类之间是不同的(同样,在2.x和3.x之间也是不同的)。
  • 如果使用__slots__,则所有类都必须具有相同的插槽。(如果你有兼容的但不同的插槽,它可能一开始看起来工作,但做了可怕的事情…)
  • 新样式类中的特殊方法定义可能不会更改。(事实上,这将在所有当前Python实现的实践中工作,但是没有文档来工作,所以…)
  • 如果您使用__new__,事情就不会像您天真地期望的那样工作。
  • 如果类有不同的元类,事情会变得更加混乱。

同时,在许多情况下,如果你认为这是必要的,还有更好的选择:

  • 使用工厂动态地创建适当类的实例,而不是创建一个基本实例,然后将其转换为派生实例。
  • 使用__new__或其他机制挂接构造。
  • 重新设计一些东西,这样就有了一个具有数据驱动行为的类,而不是滥用继承。

作为上一个非常常见的特定情况,只需将所有“变量方法”放入其实例作为“父”的数据成员保存的类中,而不是放入子类中。不要改变self.__class__ = OtherSubclass,只要做self.member = OtherSubclass(self)。如果您真的需要神奇地更改方法,那么自动转发(例如,通过__getattr__)是一个比动态更改类更常见的python习惯用法。

相关问题 更多 >