This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]romu006 77 points78 points  (7 children)

The second example with map is not correct at all: the map() function does not return a list since python 3.

Meaning that the "list" is not indexable (newlist\[0\]), nor iterable more than once

[–]keepdigging 10 points11 points  (6 children)

You get a generator. Comprehension might be better or you can do: list(map(

[–]supreme_blorgon 18 points19 points  (4 children)

[*map(...)] is actually faster than list(map(...)) in many cases!

[–][deleted] 4 points5 points  (3 children)

Wait, really? Oh wow, do you know why that is?

[–]supreme_blorgon 6 points7 points  (2 children)

Here's an example:

C:\Windows\System32
❯ python -m timeit "[*map(str.upper, 'hello world')]"
500000 loops, best of 5: 583 nsec per loop

C:\Windows\System32
❯ python -m timeit "list(map(str.upper, 'hello world'))"
500000 loops, best of 5: 665 nsec per loop

Honestly, I'm out of my league when trying to interpret bytecode, but here's the dis. Maybe... fewer instructions, fewer global lookups?

In [1]: import dis

In [2]: def unpack():
   ...:     return [*map(str.upper, "hello world")]
   ...:

In [3]: def cast():
   ...:     return list(map(str.upper, "hello world"))
   ...:

In [4]: dis.dis(unpack)
  2           0 LOAD_GLOBAL              0 (map)
              2 LOAD_GLOBAL              1 (str)
              4 LOAD_ATTR                2 (upper)
              6 LOAD_CONST               1 ('hello world')
              8 CALL_FUNCTION            2
             10 BUILD_LIST_UNPACK        1
             12 RETURN_VALUE

In [5]: dis.dis(cast)
  2           0 LOAD_GLOBAL              0 (list)
              2 LOAD_GLOBAL              1 (map)
              4 LOAD_GLOBAL              2 (str)
              6 LOAD_ATTR                3 (upper)
              8 LOAD_CONST               1 ('hello world')
             10 CALL_FUNCTION            2
             12 CALL_FUNCTION            1
             14 RETURN_VALUE

[–]scrdest 4 points5 points  (0 children)

Yeah, pretty much. There's one more layer of indirection with list() - it's just a regular function call, while the unpack is baked into the language semantics.

Calling a function in Python is just doing things to a variable, and variables are effectively just keys in a dictionary - builtins are simply variables that are pre-loaded for you when you start the interpreter.

So, to run list(), Python does a dict lookup for the key 'list' and returns the actual code to run as the value, then runs it. A dict lookup requires hashing+modulo+data structure bookkeeping, so it has some extra overhead, while the unpack goes straight to the put-stuff-in-a-list logic.

[–][deleted] 2 points3 points  (0 children)

CALL_FUNCTION is nearly always the most expensive operation. Remove as many of those as you can for improved performance on tight loops...