重写第三方模块的方法有多糟糕?
在Python中,从其他第三方模块重新定义一个类的方法到底有多糟糕呢?
其实,用户可以创建包含不确定数字的NumPy矩阵;理想情况下,我希望他们的代码在不修改的情况下运行(和处理浮点矩阵时一样);特别是,如果矩阵m
的逆矩阵仍然可以通过m.I
获得,那就太好了,尽管计算m.I
需要用我自己的代码(原来的I
方法通常是无法使用的)。
重新定义numpy.matrix.I到底有多糟糕呢?首先,这确实会影响到第三方的代码,我不太喜欢这样,因为这可能不够稳健(如果其他模块也这么做呢?……)。另一个问题是,新的numpy.matrix.I是一个包装器,这会带来一些额外的开销,而原来的numpy.matrix.I其实可以直接用来获得逆矩阵。
那继承NumPy矩阵,只改变它们的I
方法是不是更好呢?这样会迫使用户更新他们的代码,并用m = matrix_with_uncert(…)
来创建不确定数字的矩阵(而不是继续使用numpy.matrix(…)
,就像处理浮点矩阵一样),但也许为了稳健性,这种不便是值得接受的?矩阵的逆运算仍然可以通过m.I
来完成,这点很好……另一方面,如果用户可以直接用numpy.matrix()
构建所有的矩阵(无论是浮点数还是不确定数字),而不需要担心数据类型,那就更好了。
欢迎任何评论或其他方法的建议!
3 个回答
这要看你说的“重新定义”是什么意思。显然,你可以使用自己的版本,这没有问题。如果是方法的话,你也可以通过创建一个子类来重新定义它。
你还可以创建一个新方法,然后把它加到类里,这种做法叫做猴子补丁(monkey patching)。比如这样:
from amodule import aclass
def newfunction(self, param):
do_something()
aclass.oldfunction = newfunction
这样做会让所有的aclass实例都使用你新写的函数,而不是旧的那个,包括任何“第四方”模块里的实例。这种方法确实有效,而且很有用,但大家普遍认为它很丑陋,通常是最后的选择。这是因为在aclass的代码里没有任何提示说明你已经覆盖了这个方法,所以调试起来会很困难。更糟糕的是,如果两个模块都对同一个东西进行了猴子补丁,那你就会真的搞糊涂了。
子类化(这通常涉及到重写方法)通常比“猴子补丁”(把修改过的方法塞进现有的类或模块)要好得多,即使后者是可行的(内置类型,也就是用C语言实现的类型,通常会保护自己不被猴子补丁,大多数都是这样)。
举个例子,如果你的功能依赖于猴子补丁,一旦你修改的那个类升级成用C语言实现的(为了提高速度或者专门防止猴子补丁),你的功能就会崩溃,无法再升级。第三方包的维护者非常讨厌猴子补丁,因为这意味着他们会收到一些错误报告,而这些报告其实是因为用户在不知情的情况下使用了有问题的猴子补丁,导致第三方包出现问题,而后者(除非本身就有问题)其实是完美的。你也提到过可能会影响性能。
从概念上讲,“带有不确定性的数字矩阵”和“数字矩阵”是两个不同的概念。子类化能够清晰地表达这一点,而猴子补丁则试图掩盖它。这其实就是猴子补丁问题的根源:通过全局的、隐藏的方式进行的隐蔽操作,缺乏清晰和透明。所有这些实际问题在某种意义上都源于这个根本的概念问题。
我强烈建议你拒绝使用猴子补丁,选择像子类化这样的干净解决方案。