将分配分成两行仍然有效吗?
Is splitting assignment into two lines still just as efficient?
从运行时效率的角度来看,python 这些是否同样有效?
x = foo()
x = bar(x)
VS
x = bar(foo())
我有一个更复杂的问题,基本上可以归结为这个问题:显然,从代码长度的角度来看,第二种效率更高,但运行时是否也更好?如果不是,为什么不呢?
这是一个比较:
第一种情况:
%%timeit
def foo():
return "foo"
def bar(text):
return text + "bar"
def test():
x = foo()
y = bar(x)
return y
test()
#Output:
'foobar'
529 ns ± 114 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
第二种情况:
%%timeit
def foo():
return "foo"
def bar(text):
return text + "bar"
def test():
x = bar(foo())
return x
test()
#Output:
'foobar'
447 ns ± 34.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
但这只是每个案例的比较 运行%%timeit 一次。以下是每种情况下 20 次迭代的时间(以 ns 为单位的时间):
df = pd.DataFrame({'First Case(time in ns)': [623,828,634,668,715,659,703,687,614,623,697,634,686,822,671,894,752,742,721,742],
'Second Case(time in ns)': [901,786,686,670,677,683,685,638,628,670,695,657,698,707,726,796,868,703,609,852]})
df.plot(kind='density', figsize=(8,8))
据观察,随着每次迭代,差异都在缩小。此图显示 性能差异并不显着 。从可读性的角度来看,第二种情况看起来更好。
在第一种情况下,计算了两个表达式:第一个表达式首先将 foo()
中的 return 值赋给 x
,然后第二个表达式调用 bar()
在那个值上。这增加了一些开销。在第二种情况下,只评估一个表达式,同时调用两个函数并 returning 值。
它很重要微小位,但没有意义。 仅在一个测试中对函数的定义进行计时,因此必须在第一个测试中做更多的工作,从而扭曲结果。经过适当的测试,结果仅存在细微差别。使用相同的 ipython
%%timeit
魔法(IPython 7.3.0 版,CPython 3.7.2 版用于 Linux x86-64),但从每个循环测试:
>>> def foo():
... return "foo"
... def bar(text):
... return text + "bar"
... def inline():
... x = bar(foo())
... return x
... def outofline():
... x = foo()
... x = bar(x)
... return x
...
>>> %%timeit -r5 test = inline
... test()
...
...
332 ns ± 1.01 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)
>>> %%timeit -r5 test = outofline
... test()
...
...
341 ns ± 5.62 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)
inline
代码速度更快,但差异不到 10 ns/3%。进一步内联(使正文只是 return bar(foo())
)节省了 微小的 一点,但同样,它毫无意义。
这也是您所期望的;存储和加载函数局部名称是 CPython 解释器可以做的最便宜的事情,函数之间的唯一区别 is that outofline
requires an extra STORE_FAST
and LOAD_FAST
(one following the other), and those instructions are implemented internally as nothing but assignment to and reading from 一个编译时确定的 C 数组中的槽,加上一个整数增量来调整引用计数。您为每个字节代码所需的 CPython 解释器开销付费,但实际工作的成本微不足道。
要点是:不要担心速度,写哪个版本的代码会更多readable/maintainable。在这种情况下,所有的名字都是垃圾,但是如果 foo
的输出可以被赋予一个有用的名称,然后传递给 bar
,后者的输出被赋予一个不同的有用名称,如果没有这些名称,foo
和bar
不明显,不要内联。如果关系很明显,并且 foo
的输出没有从命名中获益,则将其内联。避免从局部变量存储和加载是最微观的微优化;在几乎任何情况下,它都不会成为有意义的 性能损失的原因,因此不要将代码设计决策基于它。
从运行时效率的角度来看,python 这些是否同样有效?
x = foo()
x = bar(x)
VS
x = bar(foo())
我有一个更复杂的问题,基本上可以归结为这个问题:显然,从代码长度的角度来看,第二种效率更高,但运行时是否也更好?如果不是,为什么不呢?
这是一个比较:
第一种情况:
%%timeit
def foo():
return "foo"
def bar(text):
return text + "bar"
def test():
x = foo()
y = bar(x)
return y
test()
#Output:
'foobar'
529 ns ± 114 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
第二种情况:
%%timeit
def foo():
return "foo"
def bar(text):
return text + "bar"
def test():
x = bar(foo())
return x
test()
#Output:
'foobar'
447 ns ± 34.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
但这只是每个案例的比较 运行%%timeit 一次。以下是每种情况下 20 次迭代的时间(以 ns 为单位的时间):
df = pd.DataFrame({'First Case(time in ns)': [623,828,634,668,715,659,703,687,614,623,697,634,686,822,671,894,752,742,721,742],
'Second Case(time in ns)': [901,786,686,670,677,683,685,638,628,670,695,657,698,707,726,796,868,703,609,852]})
df.plot(kind='density', figsize=(8,8))
据观察,随着每次迭代,差异都在缩小。此图显示 性能差异并不显着 。从可读性的角度来看,第二种情况看起来更好。
在第一种情况下,计算了两个表达式:第一个表达式首先将 foo()
中的 return 值赋给 x
,然后第二个表达式调用 bar()
在那个值上。这增加了一些开销。在第二种情况下,只评估一个表达式,同时调用两个函数并 returning 值。
它很重要微小位,但没有意义。 ipython
%%timeit
魔法(IPython 7.3.0 版,CPython 3.7.2 版用于 Linux x86-64),但从每个循环测试:
>>> def foo():
... return "foo"
... def bar(text):
... return text + "bar"
... def inline():
... x = bar(foo())
... return x
... def outofline():
... x = foo()
... x = bar(x)
... return x
...
>>> %%timeit -r5 test = inline
... test()
...
...
332 ns ± 1.01 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)
>>> %%timeit -r5 test = outofline
... test()
...
...
341 ns ± 5.62 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)
inline
代码速度更快,但差异不到 10 ns/3%。进一步内联(使正文只是 return bar(foo())
)节省了 微小的 一点,但同样,它毫无意义。
这也是您所期望的;存储和加载函数局部名称是 CPython 解释器可以做的最便宜的事情,函数之间的唯一区别 is that outofline
requires an extra STORE_FAST
and LOAD_FAST
(one following the other), and those instructions are implemented internally as nothing but assignment to and reading from 一个编译时确定的 C 数组中的槽,加上一个整数增量来调整引用计数。您为每个字节代码所需的 CPython 解释器开销付费,但实际工作的成本微不足道。
要点是:不要担心速度,写哪个版本的代码会更多readable/maintainable。在这种情况下,所有的名字都是垃圾,但是如果 foo
的输出可以被赋予一个有用的名称,然后传递给 bar
,后者的输出被赋予一个不同的有用名称,如果没有这些名称,foo
和bar
不明显,不要内联。如果关系很明显,并且 foo
的输出没有从命名中获益,则将其内联。避免从局部变量存储和加载是最微观的微优化;在几乎任何情况下,它都不会成为有意义的 性能损失的原因,因此不要将代码设计决策基于它。