我可以装饰显式函数调用如np.sqrt()吗?

4 投票
2 回答
622 浏览
提问于 2025-04-17 23:35

我对Python中的函数装饰器有一点了解。我觉得我的问题答案是否定的,但我想确认一下。通过一个装饰器和一个numpy数组x = np.array([1,2,3]),我可以重写x.sqrt()来改变它的行为。那么,有没有办法在Python中重写np.sqrt(x)呢?

使用场景:我正在使用quantities包。我希望能够对不确定的量进行平方根运算,而不需要更改当前使用np.sqrt()的代码。

编辑

我想修改np.sqrtquantities包中的表现,使得以下代码可以正常工作(这三行代码的输出应该是一样的,注意使用np.sqrt()时的不确定性为0)。我希望不需要最终用户修改他们的代码,而是在quantities包中正确地包装/装饰np.sqrt()。目前,许多numpy函数都被装饰过(见https://github.com/python-quantities/python-quantities/blob/ca87253a5529c0a6bee37a9f7d576f1b693c0ddd/quantities/quantity.py),但似乎只在调用x.func()时有效,而不是numpy.func(x)

import numpy as np
import quantities as pq
x = pq.UncertainQuantity(2, pq.m, 2)
print x.sqrt()
>>> 1.41421356237 m**0.5 +/- 0.707106781187 m**0.5 (1 sigma)
print x**0.5
>>> 1.41421356237 m**0.5 +/- 0.707106781187 m**0.5 (1 sigma)
print np.sqrt(x)
>>> 1.41421356237 m**0.5 +/- 0.0 dimensionless (1 sigma)

2 个回答

1

也许,正如BrenBarn所说,

np.sqrt = decorator(np.sqrt)

因为装饰器其实就是一个可以被调用的东西,它接收一个对象,然后返回一个修改过的对象。

5

猴子补丁

如果我理解你的情况没错,你的需求其实不是在装饰(也就是以标准的方式修改你自己写的函数),而是关于猴子补丁:就是在不改变别人写的函数的源代码的情况下,修改这个函数的行为。

你需要的做法大概是这样的:

import numpy as np  # provide local access to the numpy module object

original_np_sqrt = np.sqrt

def my_improved_np_sqrt(x):
  # do whatever you please, including:
  # - contemplating the UncertainQuantity-ness of x and
  # - calling original_np_sqrt as needed

np.sqrt = my_improved_np_sqrt

当然,这样做只会改变numpy.sqrt将来的含义,而不会影响过去的含义。所以,如果在你执行上面的代码之前,有人已经导入了numpy并使用了numpy.sqrt,你就无法影响他们的用法。(而且他们如何引用numpy也无所谓。)

但是在执行了上面的代码之后,所有模块中numpy.sqrt的含义(无论他们是在之前还是之后导入numpy)都会变成my_improved_np_sqrt的含义,不管那些模块的创建者是否喜欢(当然,前提是没有其他地方对numpy.sqrt进行更多的猴子补丁)。

需要注意的是:

  1. 当你做一些奇怪的事情时,Python可能会变得很奇怪!
  2. 当你做一些奇怪的事情时,Python可能会变得很奇怪!
  3. 当你做一些奇怪的事情时,Python可能会变得很奇怪!

这就是为什么猴子补丁通常不被认为是好的设计风格。所以如果你选择这种方式,确保在所有相关文档中非常明显地说明这一点。

哦,如果你不想修改其他代码,除了那些直接或间接从你自己方法中执行的代码,你可以引入一个装饰器,在调用之前进行猴子补丁,在调用之后再恢复原来的original_np_sqrt,并将这个装饰器应用到你所有相关的函数上。确保在这个装饰器中处理异常,这样在所有情况下都能真正执行恢复操作。

撰写回答