当我在页面底部看到这个说明时,我正在阅读itertools文档:
注意,可以通过将全局查找替换为定义为默认值的局部变量来优化上述许多配方。例如,dotproduct配方可以写成:
def dotproduct(vec1, vec2, sum=sum, imap=imap, mul=operator.mul): return sum(imap(mul, vec1, vec2))
这是什么意思?我是否理解将函数添加为输入变量可使程序更快或更高效地运行?
有人可以像5岁那样向我解释这个吗? :-) 提前谢谢。
答案 0 :(得分:3)
Python效率的一个问题是语言是完全动态的。例如,考虑简单的循环
def myfunc():
for i in range(10):
foo(bar(i))
似乎将调用函数foo
,结果是调用bar
十次。
但是,函数bar
可以更改foo
的内容,foo
的代码可以改变bar
的内容。因此,Python必须在每次迭代时检查foo
指向的内容以及bar
指向的内容。这需要查看模块全局变量,如果在内置(预定义)名称中没有找到任何内容。在10次迭代中的每一次。
所有全局查找都会发生同样的情况(例如,您甚至不能定义名为len
的函数,因此“隐藏”具有该名称的标准函数。)
使用局部变量时,事情更简单
def myfunc():
f = foo
b = bar
for i in range(10):
f(b(i))
原因是f
和b
是局部变量,因此获得能够进行调用的值要简单得多。 myfunc
之外的代码无法更改f
和b
指向的内容。
因此,获得一些速度的一个技巧是编写像
这样的东西def myfunc(x, sin=math.sin):
...
因此,在使用sin
时,您无需首先查找math
,然后sin
内math
。
这是一种微观优化,然而被认为是不好的风格,除非你真的发现(测量)速度是一个问题,修复已被测量以给出合理的增益,然而慢的不够严重到需要一些更激进的方法。
答案 1 :(得分:1)
好处很少:
解析函数定义时会计算默认参数。因此,当实际调用该函数时,不需要这样的计算。
在函数调用期间,当python发现在本地作用域中找不到变量时,它首先在全局变量中查找它,如果仍然没有找到,那么它将进入内置函数。最终,如果在任何地方找不到变量,它都会引发错误。
所以operator.mul
需要三次调用:首先在本地范围内搜索operator
,然后在全局范围内搜索operator
。现在,在全局范围内找到它,现在搜索mul
模块mul=operator.mul
。
在函数头中将其声明为LOAD_FAST
会将这些搜索次数减少到1。
查看字节代码:
将使用operator.itemgetter
重新加载局部变量。
其他变量如list
,list
和>>> def dotproduct(vec1, vec2, sum=sum, imap=imap, mul=operator.mul):
sum(imap(mul, vec1, vec2))
list([operator.itemgetter(1) for x in lis])
...
>>> dis.dis(dotproduct)
2 0 LOAD_FAST 2 (sum)
3 LOAD_FAST 3 (imap)
6 LOAD_FAST 4 (mul)
9 LOAD_FAST 0 (vec1)
12 LOAD_FAST 1 (vec2)
15 CALL_FUNCTION 3
18 CALL_FUNCTION 1
21 POP_TOP
3 22 LOAD_GLOBAL 0 (list)
25 BUILD_LIST 0
28 LOAD_GLOBAL 1 (lis)
31 GET_ITER
>> 32 FOR_ITER 21 (to 56)
35 STORE_FAST 5 (x)
38 LOAD_GLOBAL 2 (operator)
41 LOAD_ATTR 3 (itemgetter)
44 LOAD_CONST 1 (1)
47 CALL_FUNCTION 1
50 LIST_APPEND 2
53 JUMP_ABSOLUTE 32
>> 56 CALL_FUNCTION 1
59 POP_TOP
60 LOAD_CONST 0 (None)
63 RETURN_VALUE
需要更多呼叫。
{{1}}
答案 2 :(得分:0)
让我试试。
我认为这意味着当python尝试查找变量(函数或其他对象)时,它将使用LEGB规则
请查看此question以获取LEGB的完整说明
但是这个技巧允许python在最近的范围本地(在参数所在的位置)找到变量