Packing and Unpacking

In [1]:
def func1(x, y, z):
    print x
    print y 
    print z                 

def func2(*args):
    # Convert args tuple to a list so we can modify it
    args = list(args)
    args[0] = 'Hello'
    args[1] = 'awesome'
    func1(*args)

func2('Goodbye', 'cruel', 'world!')
Hello
awesome
world!
In [2]:
range(3, 6) # normal call with separate arguments
Out[2]:
[3, 4, 5]
In [3]:
args = [3, 6]
range(*args)  # call with arguments unpacked from a list
Out[3]:
[3, 4, 5]
In [4]:
def parrot(voltage, state='a stiff', action='voom'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it.",
    print "E's", state, "!"
In [5]:
d = {"state": "bleedin' demised", "action": "VOOM", "voltage": "four million"}
parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
In [6]:
(2 * i + 1 for i in range(3))
Out[6]:
<generator object <genexpr> at 0x108e1db40>
In [7]:
a, b, c = (2 * i + 1 for i in range(3))
a, b,c
Out[7]:
(1, 3, 5)
In [8]:
a, (b, c), d = [1, (2, 3), 4]
In [9]:
a
Out[9]:
1
In [10]:
b
Out[10]:
2
In [11]:
c
Out[11]:
3
In [12]:
d
Out[12]:
4

Swapping variables: an inside view

In [13]:
def bar(a, b, c, d):
    d, c, b, a = a, b, c, d
In [14]:
import dis
dis.dis(bar)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 LOAD_FAST                2 (c)
              9 LOAD_FAST                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_FAST               3 (d)
             21 STORE_FAST               2 (c)
             24 STORE_FAST               1 (b)
             27 STORE_FAST               0 (a)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        
In [15]:
def foo(a, b):
    a, b = b, a
dis.dis(foo)
  2           0 LOAD_FAST                1 (b)
              3 LOAD_FAST                0 (a)
              6 ROT_TWO             
              7 STORE_FAST               0 (a)
             10 STORE_FAST               1 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        
In [16]:
#https://hg.python.org/cpython/file/3696b9ae6b17/Python/peephole.c#l426
#/* Try to fold tuples of constants (includes a case for lists
#which are only used for "in" and "not in" tests).
#Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
#Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2.
#Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */
In [17]:
a, b = 1, 2                          # simple sequence assignment
print(a, b)
a, b = ['green', 'blue']             # list asqignment
print (a, b)
a, b = 'XY'                          # string assignment
print (a, b)
a, b = range(1,5,2)                  # any iterable will do
print (a, b)
(1, 2)
('green', 'blue')
('X', 'Y')
(1, 3)

Nested sequence assignment

In [18]:
(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'
print(a, b, c)

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
print(a, b, c)
('X', 'Y', 'Z')
(1, 2, 'this')
In [ ]:
(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack
In [20]:
(a,b), c = "XY", "Z"                      # ERROR -- need more than 1 value to unpack
print(a, b, c)
('X', 'Y', 'Z')
In [22]:
a, b, c = "XY", "Z"
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-22-c6bc23a802b9> in <module>()
----> 1 a, b, c = "XY", "Z"

ValueError: need more than 2 values to unpack

Extended sequence unpacking (Python 3) (switch to the other notebook)

In [23]:
*(a,*b), *c = 1,2,3,3,4,5,6,7
  File "<ipython-input-23-16db2597c1a0>", line 1
    *(a,*b), *c = 1,2,3,3,4,5,6,7
    ^
SyntaxError: invalid syntax
In [24]:
*(a,*b), c, d = 1,2,3,3,4,5,6,7
  File "<ipython-input-24-2c89d54b46b7>", line 1
    *(a,*b), c, d = 1,2,3,3,4,5,6,7
    ^
SyntaxError: invalid syntax
In [25]:
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7
  File "<ipython-input-25-dc4eb3996af1>", line 1
    *(a,*b), (c, d) = 1,2,3,3,4,5,6,7
    ^
SyntaxError: invalid syntax

Negative indexing

In [26]:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[-1]
Out[26]:
10
In [27]:
a[-3]
Out[27]:
8

List slices (a[start:end])

In [28]:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[2:8]
Out[28]:
[2, 3, 4, 5, 6, 7]

List slices with negative indexing

In [29]:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[-4:-2]
Out[29]:
[7, 8]

List slices with step (a[start:end:step])

In [30]:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[::2]
Out[30]:
[0, 2, 4, 6, 8, 10]
In [31]:
a[::3]
Out[31]:
[0, 3, 6, 9]
In [32]:
a[2:8:2]
Out[32]:
[2, 4, 6]

List slices with negative step

In [33]:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[::-1]
Out[33]:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
In [34]:
a[-2:]
Out[34]:
[9, 10]
In [35]:
a[:-2]
Out[35]:
[0, 1, 2, 3, 4, 5, 6, 7, 8]

List slice assignment

In [36]:
a = [1, 2, 3, 4, 5]
a[2:3] = [0, 0]
In [37]:
a
Out[37]:
[1, 2, 0, 0, 4, 5]
In [38]:
a[1:1] = [8, 9]
a
Out[38]:
[1, 8, 9, 2, 0, 0, 4, 5]
In [39]:
a[1:-1] = []
a
Out[39]:
[1, 5]

Naming slices (slice(start, end, step))

In [40]:
a = [0, 1, 2, 3, 4, 5]
LASTTHREE = slice(-3, None)
LASTTHREE
Out[40]:
slice(-3, None, None)
In [41]:
a[LASTTHREE]
Out[41]:
[3, 4, 5]

Iterating over list index and value pairs (enumerate)

In [42]:
a = ['Hello', 'world', '!']
for i, x in enumerate(a):
    print '{}: {}'.format(i, x)
0: Hello
1: world
2: !

Iterating over dictionary key and value pairs (dict.iteritems) in Python 2

In [43]:
m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
for k, v in m.iteritems():
    print '{}: {}'.format(k, v)
a: 1
c: 3
b: 2
d: 4

Zipping and unzipping lists and iterables

In [44]:
a = [1, 2, 3]
b = ['a', 'b', 'c']
z = zip(a, b)
z
Out[44]:
[(1, 'a'), (2, 'b'), (3, 'c')]
In [45]:
zip(*z)
Out[45]:
[(1, 2, 3), ('a', 'b', 'c')]

zip([iterable, ...]) This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The returned list is truncated in length to the length of the shortest argument sequence. When there are multiple arguments which are all of the same length, zip() is similar to map() with an initial argument of None. With a single sequence argument, it returns a list of 1-tuples. With no arguments, it returns an empty list.

In [46]:
a = [1, 2, 3, 4]    #Please note the 4 at the end
b = ['a', 'b', 'c']
z = zip(a, b)
z
Out[46]:
[(1, 'a'), (2, 'b'), (3, 'c')]
In [47]:
items = [1, 2, 3, 4, 5]
def sqr(x): return x ** 2
map(sqr, items)
Out[47]:
[1, 4, 9, 16, 25]
In [48]:
map(None, items)
Out[48]:
[1, 2, 3, 4, 5]
In [49]:
a = [1, 2, 3]
b = ['a', 'b', 'c']
map(None, a, b)
Out[49]:
[(1, 'a'), (2, 'b'), (3, 'c')]
In [50]:
map(None, a, b) == zip(a, b)
Out[50]:
True

Grouping adjacent list items using zip

In [51]:
a = [1, 2, 3, 4, 5, 6]
In [52]:
# Using iterators
group_adjacent = lambda a, k: zip(*([iter(a)] * k))
group_adjacent(a, 3)
Out[52]:
[(1, 2, 3), (4, 5, 6)]
In [ ]: