Python 是否可以从列表中调用函数而不需要调用函数的开销?
在这个简单的例子中,我想将列表理解的Python 是否可以从列表中调用函数而不需要调用函数的开销?,python,python-3.x,python-3.6,bytecode,Python,Python 3.x,Python 3.6,Bytecode,在这个简单的例子中,我想将列表理解的I系统版本信息[:] (3、6、5,‘最终’,0) >>>timeit.timeit(a) 1.2616060493517098 >>>timeit.timeit(b) 0.685117881097812 >>>dis.dis(a) 3 0加载常数1() # ... >>>dis.dis(b) 3 0加载常数1() # ... #列表理解与函数调用 #地址12的CALL_函数开销很大 >>>dis.dis(一个代码常数[1]) 3 0生成列表0 2加载速度为0
I<5
条件分解到它自己的函数中。我还想吃我的蛋糕,并拥有它,避免CALL\u函数
bytecode/在python虚拟机中创建新框架的开销
有没有办法将列表理解中的条件分解成一个新函数,但以某种方式得到一个反汇编结果,从而避免调用函数的大量开销?
import dis
import sys
import timeit
def my_filter(n):
return n < 5
def a():
# list comprehension with function call
return [i for i in range(10) if my_filter(i)]
def b():
# list comprehension without function call
return [i for i in range(10) if i < 5]
assert a() == b()
>>> sys.version_info[:]
(3, 6, 5, 'final', 0)
>>> timeit.timeit(a)
1.2616060493517098
>>> timeit.timeit(b)
0.685117881097812
>>> dis.dis(a)
3 0 LOAD_CONST 1 (<code object <listcomp> at 0x0000020F4890B660, file "<stdin>", line 3>)
# ...
>>> dis.dis(b)
3 0 LOAD_CONST 1 (<code object <listcomp> at 0x0000020F48A42270, file "<stdin>", line 3>)
# ...
# list comprehension with function call
# big overhead with that CALL_FUNCTION at address 12
>>> dis.dis(a.__code__.co_consts[1])
3 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 STORE_FAST 1 (i)
8 LOAD_GLOBAL 0 (my_filter)
10 LOAD_FAST 1 (i)
12 CALL_FUNCTION 1
14 POP_JUMP_IF_FALSE 4
16 LOAD_FAST 1 (i)
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
# list comprehension without function call
>>> dis.dis(b.__code__.co_consts[1])
3 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LOAD_CONST 0 (5)
12 COMPARE_OP 0 (<)
14 POP_JUMP_IF_FALSE 4
16 LOAD_FAST 1 (i)
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
导入dis
导入系统
导入时间信息
def my_过滤器(n):
返回n<5
定义a():
#列表理解与函数调用
如果我的_过滤器(i)在范围(10)内,则返回[i代表i]
def b():
#无函数调用的列表理解
返回[i如果i<5,则返回范围(10)内的i]
断言a()==b()
>>>系统版本信息[:]
(3、6、5,‘最终’,0)
>>>timeit.timeit(a)
1.2616060493517098
>>>timeit.timeit(b)
0.685117881097812
>>>dis.dis(a)
3 0加载常数1(<0x0000020F4890B660处的代码对象列表,文件“stdin”,第3行>)
# ...
>>>dis.dis(b)
3 0加载常数1(<0x0000020F48A42270处的代码对象列表,文件“stdin”,第3行>)
# ...
#列表理解与函数调用
#地址12的CALL_函数开销很大
>>>dis.dis(一个代码常数[1])
3 0生成列表0
2加载速度为0(.0)
>>国际热核实验堆16(至22)4
6商店快速1(i)
8加载\u全局0(我的\u筛选器)
10加载速度1(i)
12调用函数1
14如果为假,则弹出跳转4
16加载速度1(i)
18清单2
20跳/绝对4
>>22返回值
#无函数调用的列表理解
>>>dis.dis(b.u_代码__.co_常量[1])
3 0生成列表0
2加载速度为0(.0)
>>国际热核实验堆16(至22)4
6商店快速1(i)
8加载速度1(i)
10负载常数0(5)
12比较运算0(>22返回值
我愿意采用一种我永远不会在生产中使用的黑客解决方案,比如在运行时以某种方式替换字节码
换句话说,是否可以在运行时将
a
的地址8、10和12替换为b
的地址8、10和12?将注释中的所有优秀答案合并为一个
正如georg所说,这听起来像是在寻找一种内联函数或表达式的方法,而CPython中没有这样的尝试:
因此,按照“元编程”的思路,您可以构建lambda的inline和eval:
from typing import Callable
import dis
def b():
# list comprehension without function call
return [i for i in range(10) if i < 5]
def gen_list_comprehension(expr: str) -> Callable:
return eval(f"lambda: [i for i in range(10) if {expr}]")
a = gen_list_comprehension("i < 5")
dis.dis(a.__code__.co_consts[1])
print("=" * 10)
dis.dis(b.__code__.co_consts[1])
这是一个更安全一点,但仍然可能有邪恶的功能在条件下,所以你可以继续。再次,我觉得这有点乏味
从字节码开始的另一个方向来看,我的跨版本汇编程序;请看我愿意采用一种我在生产中从未使用过的黑客解决方案-值得考虑吗?@RomanPerekhrest出于教育目的-当然。老实说,我觉得你的时间最好花在学习Cython之类的东西上。我想象一下,您最好的选择是在运行时生成listcomp的代码,然后再生成它。在我看来,您似乎在寻找一种内联函数的方法……在CPython中没有这样的东西,尽管已经尝试过:
6 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LOAD_CONST 0 (5)
12 COMPARE_OP 0 (<)
14 POP_JUMP_IF_FALSE 4
16 LOAD_FAST 1 (i)
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
==========
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LOAD_CONST 0 (5)
12 COMPARE_OP 0 (<)
14 POP_JUMP_IF_FALSE 4
16 LOAD_FAST 1 (i)
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
import ast
def is_cond_str(s: str) -> bool:
try:
mod_ast = ast.parse(s)
expr_ast = isinstance(mod_ast.body[0])
if not isinstance(expr_ast, ast.Expr):
return False
compare_ast = expr_ast.value
if not isinstance(compare_ast, ast.Compare):
return False
return True
except:
return False