Python 重写类';s方法定义
假设我有一个类FooClass,它定义了一个方法foo_method。此类位于第三方库中,我希望显式重写它。我有许多从FooClass继承的类,所以我不想为每个子类重写foo_方法。相反,我希望在不深入第三方库代码的情况下重写类的方法定义。当我尝试显而易见的方法时Python 重写类';s方法定义,python,Python,假设我有一个类FooClass,它定义了一个方法foo_method。此类位于第三方库中,我希望显式重写它。我有许多从FooClass继承的类,所以我不想为每个子类重写foo_方法。相反,我希望在不深入第三方库代码的情况下重写类的方法定义。当我尝试显而易见的方法时 from thirdparty import FooClass class FooClass(object): def foo_method(): newstuff 我有奇怪的行为——没有执行错误之类的。我错过什么了
from thirdparty import FooClass
class FooClass(object):
def foo_method():
newstuff
我有奇怪的行为——没有执行错误之类的。我错过什么了吗 您正在本地重新定义类;这不会改变原始类,也不会影响在其他地方使用它的任何人 您可以monkeypatch类上的方法:
from thirdparty import FooClass
orig_foo_method = FooClass.foo_method
def new_foo_method(self):
# do new stuff
# perhaps even call orig_foo_method(self)
FooClass.foo_method = new_foo_method
现在,任何试图在Python解释器中的
FooClass
实例上调用.foo\u method()
的代码都会找到并调用新的foo\u method()
方法。首先想到的是使用简单的继承
from thirdparty import FooClass
class MyUpdatedClass(FooClass):
def foo_method():
newstuff
这不会更新任何试图在别处导入
FooClass
的第三方代码。但是,如果使用FooClass的所有代码都在子类中,这比monkeypatching要好得多。应该更小心地使用MyUpdatedClass
来替换此类第三方代码。让monkeypatching继续进行,并且从类post monkeypatching派生出来,这是错误的,也是非常糟糕的。这是一个“正确”的答案,这意味着FooClass
的所有其他用途都需要改变以适应这一点(是的,即使在有大量现有单元测试的大型项目中,等等,依靠常规的FooClass
)。@EMS如果您想修改未编写的代码的行为,这将无法解决问题。这里绝对有一个地方可以用来打蒙基补丁,而且在这里,如果小心使用和考虑的话。我不确定我是否理解。在这种情况下,请使用适配器或代理模式,但仍要维护FooClass
的子类,以防您希望拥有定义不同的方法(或额外方法)的FooClass
实例。这似乎仍然比蒙基补丁要好。这相当有效——我以前做过。但请注意,任何执行此操作的人都需要确保在尝试使用修补模块之前加载了修补模块(或以其他方式调用了修补代码)class@Marcin:绝对;任何存储原始方法引用的代码(绑定或其他)也不会看到修补过的新方法。这还包括修补过该方法的任何实例。我想这是不常见的,但如果有人变得疯狂,这是可能的。你还必须非常小心酸洗/取消酸洗和其他对象的持久性。当一些库试图实现“流畅的界面”模式时,可能会特别痛苦,因此,现在还必须重写所有变异并返回原始类的变异版本的方法,以确保在链中每次调用后monkeypatch仍然存在。@Marcin:您是指在实例字典中以相同名称存储函数或方法对象的实例吗?是的,它们也不会在类上看到修补的方法。我们正在进一步远离可能出现的情况。我认为您需要更清楚地理解“显式覆盖”的含义。你的意思是,改变foo_方法
在任何地方的工作方式(即使是完全在thirdparty
模块中使用)?或者您的意思是,更改foo_方法的工作方式,但仅限于我创建的FooClass
的某些实例。首先,使用“猴子补丁”,如Martijn Pieters的回答。第二,使用继承,就像Osh的回答一样。@Blckknght“完全在thirdparty
模块中使用”是什么意思?thirdparty
模块没有导入。@eyquem:我的意思是,如果其他一些代码导入thirdparty
并调用thirdparty.do_stuff()
来创建一些FooClass
实例,这些实例是否应该得到更改的foo_方法
行为?如果你继承了他们的遗产,他们就不会继承。当然,如果没有这样的其他代码(而且你知道永远不会有),或者如果thirdparty
模块中没有创建FooClass
实例的函数,这可能是不相关的。@Blckknght加上“if some other code imports thirdparty”,您是指通用代码中的另一部分,其中写入了第三方导入FooClass的?也就是说在同一个脚本中?@eyquem:可能在不同的模块中编写代码。通常,您不会在第三方导入类的中导入另一个第三方
模块。但是,如果您的程序有许多模块,那么像thirdparty
这样的东西以不同的方式多次导入到不同的地方并不少见。因此,重申我的观点:如果您的程序只有一个模块使用FooClass
,或者如果您希望其他模块能够继续使用常规实现,请使用继承。如果您有多个模块,并且希望覆盖所有模块的foo\u方法
,请使用monkey补丁。