ipython %timeit“赋值前引用的局部变量‘a’”

man*_*ndy 6 python ipython timeit

我正在尝试运行以下代码,但我得到了local variable 'a' referenced before assignment.

a = [x for x in range(10)]
b = [x for x in range(10)]
%timeit a+=b
Run Code Online (Sandbox Code Playgroud)

该声明无需%timeit魔法即可发挥作用。

我有什么遗漏的吗?

谢谢。

hpa*_*ulj 4

您期望它做什么?

\n\n

在时间之外它会:

\n\n
In [188]: a += b\nIn [189]: a\nOut[189]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n
Run Code Online (Sandbox Code Playgroud)\n\n

我尝试初始化x,并得到一个接近无限循环,最终以内存错误结束

\n\n
In [192]: %%timeit x=a\n     ...: x += b\n\nIn [194]: len(a)\nOut[194]: 529076630\n
Run Code Online (Sandbox Code Playgroud)\n\n

换句话说,每个 timeit 循环都将另一个b值列表连接到x(并通过扩展a),结果是非常长的循环。我怀疑是某个人x+=b速度太快,导致timeit选择循环多次。

\n\n

让我们创建一个a新的 every 循环:

\n\n
In [196]: %%timeit\n     ...: a = [x for x in range(10)]\n     ...: a += b\n     ...: \n1.91 \xc2\xb5s \xc2\xb1 4.82 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这也会产生内存错误:

\n\n
In [197]: %%timeit a = [x for x in range(10)]\n     ...: a += b\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我控制循环次数:

\n\n
In [202]: %%timeit -n 100 a = [x for x in range(10)]\n     ...: a += b\n     ...: \n     ...: \n208 ns \xc2\xb1 11 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

随着ns时间的推移,我明白为什么默认循环如此之大。

\n\n

我之前没有尝试过对普通进行计时a+=...(甚至没有使用 numpy 数组),但显然它需要某种本地初始化a,无论是在循环内还是在初始化块中。但请务必记住,定时操作可能会执行多次(-r 和 -n 参数或默认值)。因此,任何就地操作都可能导致全局值发生一些变化。在这种情况下timeit,可能会试图通过期待某种“局部”变量来保护我们免受这种意外增长的影响。

\n\n
\n\n

让我们尝试一下a+b,但有一个任务:

\n\n
In [215]: c=np.zeros(10)\nIn [216]: a\nOut[216]: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])\nIn [217]: b\nOut[217]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\nIn [218]: %timeit c = a+b\n5.33 \xc2\xb5s \xc2\xb1 105 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\nIn [219]: c\nOut[219]: array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,全局c没有改变。该分配是临时本地的c- 即使同名的全局可用。

\n\n

作为一般规则,在定时循环内执行的计算不应泄漏到循环之外。你必须像我在内存错误循环中所做的那样明确一些,或者在这里

\n\n
In [222]: %%timeit x = c\n     ...: x += b\n     ...: \n9.04 \xc2\xb5s \xc2\xb1 238 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\nIn [223]: c\nOut[223]: \narray([       0.,   811111.,  1622222.,  2433333.,  3244444.,  4055555.,\n        4866666.,  5677777.,  6488888.,  7299999.])\n
Run Code Online (Sandbox Code Playgroud)\n\n

或在这里:

\n\n
In [224]: c=np.zeros(10)\nIn [225]: %%timeit x = c\n     ...: x[:] = a+b\n\n7.84 \xc2\xb5s \xc2\xb1 199 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\nIn [226]: c\nOut[226]: array([  1.,   3.,   5.,   7.,   9.,  11.,  13.,  15.,  17.,  19.])\n
Run Code Online (Sandbox Code Playgroud)\n\n

两者都使用对已链接到可变全局变量的局部变量的就地赋值。

\n

  • 我只是希望它能计时“a+=b”。以下命令有效: %timeit a+b %timeit a.extend(b) 但是: %timeit a+=b 返回错误消息“局部变量 'a' 在赋值之前引用。”我只是想了解为什么“a”在后者中未知,但适用于前者。 (4认同)