#28909: Use unpacking generalizations added in Python 3.5
-------------------------------------+-------------------------------------
               Reporter:  Nick Pope  |          Owner:  Nick Pope
                   Type:             |         Status:  assigned
  Cleanup/optimization               |
              Component:             |        Version:  master
  Uncategorized                      |
               Severity:  Normal     |       Keywords:  unpacking
           Triage Stage:             |      Has patch:  1
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 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]
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/28909>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.8fe14e82ddb962324d7519ef4e2732d7%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to