Python: short coordinate function unexpectedly changes incoming argument value

Question:

The goal here is to take some list of coordinates, like [[1,2],[3,4],[7,1]], then figure out how big the canvas should be if you want to print all these coords. Take the maximal bottom left coordinate and minimal upper right coordinate that will snugly fit a canvas to these points. In the above list, for example, we’re looking for [[1,1],[7,4]], which defines the smallest rectangle where all those points will fit.

In the middle of this function, I’m seeing the incoming “board” assigned a new value.

def print_board(board):
    # import pdb; pdb.set_trace()
    dimensions = None
    for i in board:
        if dimensions == None:
            dimensions = [i, i]
        else:
            dimensions[0][0] = min(dimensions[0][0], i[0])
            #'board' is redefined !!!
            dimensions[0][1] = min(dimensions[0][1], i[1]) 
            #dimensions[1][0] = max(dimensions[1][0], i[0])
            #dimensions[1][1] = max(dimensions[1][1], i[1])
    # (after we get the canvas size
    # we print the canvas with the points on it
    # but we never make it that far without an error)

As the for loop moves through the coordinates in the incoming board, it seems to be setting board[0] to whatever coordinate it’s looking at at the time. So [[1,2],[3,4],[7,1]] will change first to [[3,4],[3,4],[7,1]], then to [[7,1],[3,4],[7,1]].

I wouldn’t expect board to change at all.

(Python 3.2.2)

Asked By: Brownbat

||

Answers:

When you do

dimensions = [i, i]

you’re setting both items in dimensions to the first point in your board — not making copies of that point.

Then when you do

        dimensions[0][0] = min(dimensions[0][0], i[0])
        dimensions[0][1] = min(dimensions[0][1], i[1]) 

you’re updating that same point — the first point in your board — to the results of the min functions.

Try something like this, instead:

def print_board(board):
    xs, ys = zip(*board) # separate out the x and y coordinates
    min_x, max_x = min(xs), max(xs) # find the mins and maxs
    min_y, max_y = min(ys), max(ys)
    dimensions = [[min_x, min_y], [max_x, max_y]] # make the dimensions array
Answered By: agf

As an extension of agfs answer, you can use numpy for even more efficient and succinct code:

import numpy as np
def print_board(board):
    a = np.array(board)
    return [a.min(axis=0).tolist(), a.max(axis=0).tolist()]

If your board is a numpy array already, and you let the function return a tuple of numpy arrays, it shortens even more:

def print_board(board):
    return board.min(axis=0), board.max(axis=0)
Answered By: Lauritz V. Thaulow
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.