Why doesn't timeit work in my code snippet?

I think these 3 are logically equivalent, returning the set {1, 3, 4} :

 set(sum(((1, 3), (4,), (1,)), ())) set(sum([[1, 3], [4], [1]], [])) functools.reduce(operator.or_, ({1, 3}, {4}, {1}), set()) 

But when I try to test everyone's performance in ipython (v1.2.1 on python 3.4.0), the timeit magic stops working.

 In [1]: from operator import or_; from functools import reduce In [2]: timeit set(sum([[1, 3], [4], [1]], [])) 1000000 loops, best of 3: 604 ns per loop In [3]: timeit set(sum(((1, 3), (4,), (1,)), ())) 1000000 loops, best of 3: 330 ns per loop In [4]: timeit reduce(or_, ({1, 3}, {4}, {1}), set()) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-4-83628f6293f3> in <module>() ----> 1 get_ipython().magic('timeit reduce(or_, ({1, 3}, {4}, {1}), set())') /usr/lib/python3/dist-packages/IPython/core/interactiveshell.py in magic(self, arg_s) 2164 magic_name, _, magic_arg_s = arg_s.partition(' ') 2165 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC) -> 2166 return self.run_line_magic(magic_name, magic_arg_s) 2167 2168 #------------------------------------------------------------------------- /usr/lib/python3/dist-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line) 2085 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals 2086 with self.builtin_trap: -> 2087 result = fn(*args,**kwargs) 2088 return result 2089 /usr/lib/python3/dist-packages/IPython/core/magics/execution.py in timeit(self, line, cell) /usr/lib/python3/dist-packages/IPython/core/magic.py in <lambda>(f, *a, **k) 190 # but it overkill for just that one bit of state. 191 def magic_deco(arg): --> 192 call = lambda f, *a, **k: f(*a, **k) 193 194 if isinstance(arg, collections.Callable): /usr/lib/python3/dist-packages/IPython/core/magics/execution.py in timeit(self, line, cell) 929 number = 1 930 for i in range(1, 10): --> 931 if timer.timeit(number) >= 0.2: 932 break 933 number *= 10 /usr/lib/python3.4/timeit.py in timeit(self, number) 176 gc.disable() 177 try: --> 178 timing = self.inner(it, self.timer) 179 finally: 180 if gcold: <magic-timeit> in inner(_it, _timer) TypeError: unsupported operand type(s) for |: 'set' and 'tuple' 

What's going on here? Also fails in 2.7. I cannot reproduce this using the vanilla python timeit.timeit .

+6
source share
1 answer

It looks like an error in IPython.

First workaround

remove the brackets so that the call looks like

 timeit reduce(or_, ({{1, 3}}, {{4}}, {{1}}), set()) 

Now the problem

If you see a call stack, before the call is cascaded to timeit.py, it goes through

 /usr/lib/python3/dist-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line) 2085 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals 2086 with self.builtin_trap: -> 2087 result = fn(*args,**kwargs) 2088 return result 

Now, if you reference this source, you can see that before the arguments are passed to the timeit function, it is formatted to extend Python variables in the string

  magic_arg_s = self.var_expand(line, stack_depth) # Put magic args in a list so we can call with f(*a) syntax args = [magic_arg_s] 

self.var_expand calls DollarFormatter() as a for-matter function, whose doc line says something in the following lines

 class DollarFormatter(FullEvalFormatter): """Formatter allowing Itpl style $foo replacement, for names and attribute access only. Standard {foo} replacement also works, and allows full evaluation of its arguments. 

So, here's why, the set is interpreted as a standard replacement {foo} and converted to a tuple (if the values ​​are separated by a comma), or a constant that makes the expression

 reduce(or_, ((1, 3), 4, 1), set()) 

which does not match the course.

+4
source

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


All Articles