Search code examples
python-3.xscopeiterable-unpacking

Unexpected tuple unpacking, possible scoping issue/confusion


I'm using python 3.6.

I'm really confused about the unpacking in line 09 and 10. I expect dy_outer on line 09 to equal (0, -1) and dy_inner on line 09 to equal (side-1, 0). Similarly, I expect dx_outer and dx_inner on line 10 to equal (0, -1) and (side-1, 0) respectively.

However, when the set_trace goes off, instead I see that that values of dy_outer and dy_inner from line 09 are equal to 0 and -1 respectively. And same goes with dx_outer and dx_inner from line 10; they're equal to 0 and -1 respectively as well.

offsets is a tuple of tuples, so when you iterate over it, it should unpack each tuple, not each element from the first tuple.

Is my intuition about Python scoping incorrect?

01 def cover(board, lab = 1, top = 0, left =0, side = None):
02    if side is None:
03        side = len(board)
04    
05    s = side // 2
06    
07    offsets = (0, -1), (side-1, 0)
08    
09    for dy_outer, dy_inner in offsets:
10        for dx_outer, dx_inner in offsets:
11            set_trace()
12            if not board[top+dy_outer][left+dx_outer]:
13                board[top + s + dy_inner][left+s+dx_inner] = lab
14    lab += 1
15    if s > 1:
16        for dy in [0,s]:
17            for dx in [0, s]:
18                lab = cover(board, lab, top+dy, left+dx, s)
19    return lab

Solution

  • The mystery has three parts. First, the offsets structure is a 2-D array:

    ((0, -1),
     (side-1, 0))
    

    The second part is that the for-loop will loop only over the rows:

    >>> for row in offsets:
            print(row)
    
    (0, -1)
    (18, 0)
    

    The third part is that the unpacking just unpacks the columns on a given row:

    >>> for column_zero, column_one in offsets:
            print('Column 0:', column_zero)
            print('Column 1:', column_one)
            print()
    
    Column 0: 0
    Column 1: -1
    
    Column 0: 18
    Column 1: 0
    

    Putting the pieces together shows that your intuition was correct:

    print('dy_o', 'dy_i', 'dx_o', 'dx_i', sep='\t\t')
    print('----', '----', '----', '----', sep='\t\t')
    for dy_outer, dy_inner in offsets:
        for dx_outer, dx_inner in offsets:
            print(dy_outer, dy_inner, dx_outer, dx_inner, sep='\t\t')
    

    gives the expected output:

    dy_o            dy_i            dx_o            dx_i
    ----            ----            ----            ----
    0               -1              0               -1
    0               -1              18              0
    18              0               0               -1
    18              0               18              0     
    

    Hope this takes the riddle out of the mystery, inside an enigma :-)