Does Python optimize lambda x: x

So, I wrote code for max / min, which assumes a bottleneck in data generation (otherwise I would just use maxand min), and it performs a key function, which, if not set, uses the identification function:

if key is None:
    key = lambda x: x

and then later:

for i in iterable:
    key_i = key(i)

And since the bottleneck is on the generator, the question can be debatable, but if there is no key, I call lambda x: xfor each element. I would suggest that Python can optimize this identification function. Can someone tell me if this happens? Or, if it is not, how expensive is it? Is there a way to do this better without doubling the number of lines (e.g. triple operators)?

+4
source share
4 answers

Good question! The optimizer could see that foo could be an identification function under certain predictable conditions and create an alternative way to replace its call with a known result

Let's look at the operation codes:

>>> def foo(n):
...     f = lambda x:x
...     return f(n)
... 
>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (<code object <lambda> at 0x7f177ade7608, file "<stdin>", line 2>) 
              3 MAKE_FUNCTION            0 
              6 STORE_FAST               1 (f) 

  3           9 LOAD_FAST                1 (f) 
             12 LOAD_FAST                0 (n) 
             15 CALL_FUNCTION            1 
             18 RETURN_VALUE         

CPython (versions 2.7 and 3.3) does not seem to optimize the lambda call. Perhaps a different implementation?

>>> dis.dis(lambda x:x)
  1           0 LOAD_FAST                0 (x) 
              3 RETURN_VALUE   

The identification function does not do much. So you basically have 2 LOAD_FAST, 1 CALL_FUNCTION and 1 RETURN_VALUE to optimize every time you call the authentication function, instead of creating a reliable alternative path (which can be more complicated than the interpreter seems to say, as @viraptor says).

Perhaps the else path in python code is only better.

, min/max, , , . n n * 4, !

+8

, lambda x: x , - - , . , , , . .

, . , , , , . , , , Python. Peephole , , :

LOAD_FAST, , a CALL_FUNCTION. something(args). . , , , , , .

, Peephole, . - , , , , , . -, , LOAD_GLOBAL True LOAD_CONST True.

, , . ; , lambdas, , . , Peephole (?) . , , -.

. , , -.

+3

, , , , : ( , ).

, Python 3:

import timeit

setup = """
def control_flow(iterable, key=None):
    if key is None:
        for i in iterable:
            pass
    else:
        for i in iterable:
            key_i = key(i)

def identity_lambda(iterable, key=None):
    if key is None:
        key = lambda x: x
    for i in iterable:
        key_i = key(i)

def ternary(iterable, key=None):
    for i in iterable:
        key_i = key(i) if key else i
"""
print('Testing no lambda')
timeit.timeit('control_flow(range(100))', setup=setup)
timeit.timeit('identity_lambda(range(100))', setup=setup)
timeit.timeit('ternary(range(100))', setup=setup)
print('Testing with lambda')
timeit.timeit('control_flow(range(100), lambda x: -x)', setup=setup)
timeit.timeit('identity_lambda(range(100), lambda x: -x)', setup=setup)
timeit.timeit('ternary(range(100), lambda x: -x)', setup=setup)

:

Testing no lambda
1.8421741100028157
10.212458187001175
3.39080909700715

Testing with lambda
14.262093641998945
14.405747531011002
14.198169080002117

, , , lambda x: x, , . , , , , Python , , .

+2

CPython . ? , .

+1

Source: https://habr.com/ru/post/1530894/


All Articles