Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 是否可以从列表中调用函数而不需要调用函数的开销?_Python_Python 3.x_Python 3.6_Bytecode - Fatal编程技术网

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