尝试在循环中调用 None 上的方法对性能的影响

Performance impact on trying to call methods on None in a loop

让我们想象一下下面的代码

from pymaybe import maybe

def compute_something():
return np.sin(2)

def foo(observer=None):
    observer = maybe(observer)
    for i in range(0, int(1e3)):
         something = compute_something()
         observer.observe(something)  # Comment this line for comparison

所以基本上我正在尝试某种控制反转,我传递一个实现接口的对象(这里是一个方法 observe),但我想保留用户什么都不传递的可能性(因此默认参数值被定义为 maybe(None).

问题是:我应该期待一些性能影响吗?

我使用 timeit 来比较两者(调用和不调用 observe):

Without a call to observe:

1.77 ms ± 5.38 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

With a call to observe:

3.27 ms ± 22.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

影响似乎很小,我假设 pymaybe 只是一个 try 块,所以我也与以下代码进行了比较:

def foo(observer=None):
    observer = None
    for i in range(0, int(1e3)):
         something = compute_something()
         try:
             observer.observe(something)
         except:
            pass
foo()

结果如下:

With a call to observe:

2.73 ms ± 66.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

所以我得出以下结论(不足为奇): - pymaybe 通过在 try 块周围增加一层来增加一些开销 - try 块本身对性能有不可忽略的影响

如果我上面的 assumptions/conclusions 是正确的,有什么方法可以进一步提高性能?

我可以复制代码吗?

有没有办法 "remove" 在运行时调用 None 上的方法(在循环执行期间值不会改变的循环中)?

第一种方法是使用 if 语句,正如 Scott Hunter 所建议的那样 :

def foo(observer=None):
    for i in range(0, int(1e3)):
         something = compute_something()
         if observer:
             observer.observe(something)

您还可以在 observer.observe 周围使用默认不执行任何操作的包装器:

def foo(observer=None):
    try:
        observe = observer.observe
    except AttributeError:
        def observe(x):
            pass
    for i in range(0, int(1e3)):
         something = compute_something()
         observe(something)

在我的实验中,两种方法的 运行 次相似。