猴子补丁 Python 类
Posted
技术标签:
【中文标题】猴子补丁 Python 类【英文标题】:Monkey-patch Python class 【发布时间】:2011-04-15 11:30:40 【问题描述】:我有一个类,位于一个单独的模块中,我无法更改。
from module import MyClass
class ReplaceClass(object)
...
MyClass = ReplaceClass
这不会改变 MyClass 除了这个文件之外的任何地方。但是,如果我要添加这样的方法
def bar():
print 123
MyClass.foo = bar
这将起作用,并且 foo 方法将在其他任何地方可用。
如何完全替换类?
【问题讨论】:
【参考方案1】:import module
class ReplaceClass(object):
....
module.MyClass = ReplaceClass
【讨论】:
+1 更快。只要确保它在任何使用module.MyClass
的导入之前运行
更快并不意味着更正确,但我并不认为这需要额外的解释。一旦你知道如何做,区别应该是很明显的。
确保您是“第一个”导入课程。已经对“旧”类的引用不会被替换!
非常感谢! Ivo 指出这实际上是我的错误。我没有注意包含的顺序,这在重载或向类中添加方法时并不重要,因为它们仅在使用时才被调用。
这将仅影响此文件内还是全局更改行为【参考方案2】:
避免使用from ... import
(可怕的;-) 方法来获取裸名,因为您最需要的是合格 名称。一旦你以正确的 Pythonic 方式做事:
import module
class ReplaceClass(object): ...
module.MyClass = ReplaceClass
这样,您就在修补 module 对象,这是您所需要的,并且在该模块用于其他人时将起作用。使用from ...
表单,您只是没有拥有 模块对象(一种看待大多数人使用from ...
的明显缺陷的方法),因此您的情况显然更糟; -);
我推荐使用from
语句的一种方法是从包中导入模块:
from some.package.here import amodule
所以你仍然会得到模块对象,并且会为该模块中的所有名称使用限定名称。
【讨论】:
+1 但请注意module.MyClass=
,虽然它会替换模块中的类,但不会替换已经完成from module import MyClass
的任何其他模块中的类。他们将有自己的单独副本。猴子补丁很糟糕。 from module import
也很糟糕 mmkay;两者同时是出错的秘诀。
@bobince,绝对!如果代码的其他部分也使用我讨厌的from module import MyClass
,那么在“一团糟”和“不可能”之间找到和胡闹所有这些部分(@987654331 @ 可以有时帮助...但不是总是!-)。是的,它们都是不好的做法,但有时猴子补丁可能是最轻的,而使用 from
语句创建“人造裸名”可以总是省去。
我可以将这些字符串添加到 init.py 以确保首先发生这种替换吗?【参考方案3】:
我只是一个鸡蛋。 . . .也许对非新手来说很明显,但我需要from some.package.module import module
成语。
我不得不修改 GeneralHelpfulClass 的一种方法。这失败了:
import some.package.module
class SpeciallyHelpfulClass(some.package.module.GenerallyHelpfulClass):
def general_method(self):...
some.package.module.GenerallyHelpfulClass = SpeciallyHelpfulClass
代码运行,但没有使用重载到 SpeciallyHelpfulClass 的行为。
这行得通:
from some.package import module
class SpeciallyHelpfulClass(module.GenerallyHelpfulClass):
def general_method(self):...
module.GenerallyHelpfulClass = SpeciallyHelpfulClass
我推测 from ... import
成语“获取模块”,正如 Alex 所写,因为它将被包中的其他模块拾取。进一步推测,较长的虚线引用似乎将模块带入了通过长虚线引用导入的名称空间,但不会更改其他名称空间使用的模块。因此,对导入模块的更改只会出现在进行更改的名称空间中。就好像同一个模块有两个副本,每个副本在略有不同的引用下可用。
【讨论】:
+1 对此,因为我发现谈论扩展要修补的类然后替换它比仅仅扩展 Object 并替换它更有帮助。【参考方案4】:import some_module_name
class MyClass(object):
... #copy/paste source class and update/add your logic
some_module_name.MyClass = MyClass
替换时最好不要更改类的名称,因为不知何故有人可能使用 getattr 引用了它们 - 这将导致如下所示的失败
getattr(some_module_name, 'MyClass')
--> 如果您将 MyClass 替换为 ReplaceClass ,则会失败!
【讨论】:
1) 这只会在执行导入的模块中生效:添加as foo
无济于事。 2)您关于 getattr 的论点不仅令人困惑(因为您对名称的使用不一致)而且也是错误的,并且将类的名称与类本身混淆了。如果我 import somemodule; somemodule.someclass = someotherclass
,那么稍后,在另一个模块中,getattr(somemodule, 'someclass')
将返回 someotherclass
,这正是 OP 想要的。 3) 复制粘贴编程丑陋且容易出错。
@AaronMcSmooth,同意!,现在我已经编辑了答案,但是由于 OP 想要替换整个类,所以我编写了复制/粘贴和添加/更新逻辑,因为它不可能继承那个原始类,因为我们要替换它,如果 OP 只想更新几行逻辑,那么他需要从原始源获取其余代码!
现在您的解决方案与已经提出的两个解决方案相同,但添加了一个(仍然!)关于getattr
的错误陈述的缺陷另外,为什么我们不能从一个类继承我们要“替换”? class A(object):pass; class B(A): pass; A = B; a = A()
.
@shajapan:什么递归?这似乎没有问题。有什么我不知道的问题吗?以上是关于猴子补丁 Python 类的主要内容,如果未能解决你的问题,请参考以下文章