﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
28909	Use unpacking generalizations added in Python 3.5	Nick Pope	Nick Pope	"Now that master is Python 3.5+ we can look to using unpacking generalizations for dict, list, set and tuple.

https://docs.python.org/3.5/whatsnew/3.5.html#whatsnew-pep-448

One benefit to this is that slow function/method calls can avoided and specific operations for unpacking are used instead:

{{{#!console
$ python -c ""import dis; dis.dis(\""d0 = {'a': 1}; d1 = {'b': 2}; x = {**d0, **d1}\"")""
  1           0 LOAD_CONST               0 ('a')
              2 LOAD_CONST               1 (1)
              4 BUILD_MAP                1
              6 STORE_NAME               0 (d0)
              8 LOAD_CONST               2 ('b')
             10 LOAD_CONST               3 (2)
             12 BUILD_MAP                1
             14 STORE_NAME               1 (d1)
             16 LOAD_NAME                0 (d0)
             18 LOAD_NAME                1 (d1)
             20 BUILD_MAP_UNPACK         2
             22 STORE_NAME               2 (x)
             24 LOAD_CONST               4 (None)
             26 RETURN_VALUE
    
$ python -c ""import dis; dis.dis(\""d0 = {'a': 1}; d1 = {'b': 2}; x = dict(d0, **d1)\"")""
  1           0 LOAD_CONST               0 ('a')
              2 LOAD_CONST               1 (1)
              4 BUILD_MAP                1
              6 STORE_NAME               0 (d0)
              8 LOAD_CONST               2 ('b')
             10 LOAD_CONST               3 (2)
             12 BUILD_MAP                1
             14 STORE_NAME               1 (d1)
             16 LOAD_NAME                2 (dict)
             18 LOAD_NAME                0 (d0)
             20 BUILD_TUPLE              1
             22 LOAD_NAME                1 (d1)
             24 CALL_FUNCTION_EX         1
             26 STORE_NAME               3 (x)
             28 LOAD_CONST               4 (None)
             30 RETURN_VALUE
    
$ python -c ""import dis; dis.dis(\""d0 = {'a': 1}; d1 = {'b': 2}; x = d0.copy(); x.update(d1)\"")""
  1           0 LOAD_CONST               0 ('a')
              2 LOAD_CONST               1 (1)
              4 BUILD_MAP                1
              6 STORE_NAME               0 (d0)
              8 LOAD_CONST               2 ('b')
             10 LOAD_CONST               3 (2)
             12 BUILD_MAP                1
             14 STORE_NAME               1 (d1)
             16 LOAD_NAME                0 (d0)
             18 LOAD_ATTR                2 (copy)
             20 CALL_FUNCTION            0
             22 STORE_NAME               3 (x)
             24 LOAD_NAME                3 (x)
             26 LOAD_ATTR                4 (update)
             28 LOAD_NAME                1 (d1)
             30 CALL_FUNCTION            1
             32 POP_TOP
             34 LOAD_CONST               4 (None)
             36 RETURN_VALUE
    
$ python -m timeit ""d0 = {'a': 1}; d1 = {'b': 2}; x = {**d0, **d1}""
1000000 loops, best of 3: 0.24 usec per loop
    
$ python -m timeit ""d0 = {'a': 1}; d1 = {'b': 2}; x = dict(d0, **d1)""
1000000 loops, best of 3: 0.39 usec per loop
    
$ python -m timeit ""d0 = {'a': 1}; d1 = {'b': 2}; x = d0.copy(); x.update(d1)""
1000000 loops, best of 3: 0.45 usec per loop
}}}

Obviously these are fairly contrived examples, and in many cases there can be multiple calls to methods such as {{{dict.update()}}} or {{{list.extend()}}}.

Here are examples of a number of changes that could be made:

{{{#!diff
# Unpack directly into initial definition:
-d = {...}
-d.update(extra)
+d = {..., **extra}

# Unpack instead of using dict() to combine (#1):
-d = dict(original, **extra)
+d = {**original, **extra}

# Unpack instead of using dict() to combine (#2):
-d = dict(original, ...)
+d = {**original, ...}

# Unpacking peforms a shallow copy like dict():
-d = dict(original)
-d.update(extra)
+d = {**original, **extra}

# Unpacking peforms a shallow copy like dict.copy():
-d = original.copy()
-d.update(extra)
+d = {**original, **extra}

# Unpacking peforms a shallow copy like copy.copy():
-import copy
-d = copy.copy(original)
-d.update(extra)
+d = {**original, **extra}

# More complex examples that become simple:
-d = dict(original, ...)
-d.update(extra or {})
+d = {**original, ... **(extra or {})}

# Unpacking for sets also makes things simple:
-s = {...}
-s.update(extra)
-s.update(override)
+s = {..., *extra, *override}

# Unpacking for lists also makes things simple:
-l = [...]
-l.extend(extra)
-l.extend(override)
+l = [..., *extra, *override]
}}}


"	Cleanup/optimization	closed	Core (Other)	dev	Normal	fixed	unpacking		Ready for checkin	1	0	0	0	0	0
