Question

I want to pass a list into function by value. By default, lists and other complex objects passed to function by reference. Here is some desision:

def add_at_rank(ad, rank):
    result_ = copy.copy(ad)
    .. do something with result_
    return result_

Can this be written shorter? In other words, I wanna not to change ad.

Was it helpful?

Solution

You can use [:], but for list containing lists(or other mutable objects) you should go for copy.deepcopy():

lis[:] is equivalent to list(lis) or copy.copy(lis), and returns a shallow copy of the list.

In [33]: def func(lis):
    print id(lis)
   ....:     

In [34]: lis = [1,2,3]

In [35]: id(lis)
Out[35]: 158354604

In [36]: func(lis[:])
158065836

When to use deepcopy():

In [41]: lis = [range(3), list('abc')]

In [42]: id(lis)
Out[42]: 158066124

In [44]: lis1=lis[:]

In [45]: id(lis1)
Out[45]: 158499244  # different than lis, but the inner lists are still same

In [46]: [id(x) for x in lis1] = =[id(y) for y in lis]
Out[46]: True

In [47]: lis2 = copy.deepcopy(lis)  

In [48]: [id(x) for x in lis2] == [id(y) for y in lis]  
Out[48]: False

OTHER TIPS

This might be an interesting use case for a decorator function. Something like this:

def pass_by_value(f):
    def _f(*args, **kwargs):
        args_copied = copy.deepcopy(args)
        kwargs_copied = copy.deepcopy(kwargs)
        return f(*args_copied, **kwargs_copied)
    return _f

pass_by_value takes a function f as input and creates a new function _f that deep-copies all its parameters and then passes them to the original function f.

Usage:

@pass_by_value
def add_at_rank(ad, rank):
    ad.append(4)
    rank[3] = "bar"
    print "inside function", ad, rank

a, r = [1,2,3], {1: "foo"}
add_at_rank(a, r)
print "outside function", a, r

Output:

"inside function [1, 2, 3, 4] {1: 'foo', 3: 'bar'}"
"outside function [1, 2, 3] {1: 'foo'}"

A shallow copy is usually good enough, and potentially mush faster than deep copy.

You can take advantage of this if the modifications you are making to result_ are not mutating the items/attributes it contains.

For a simple example if you have a chessboard

board = [[' ']*8 for x in range(8)]

You could make a shallow copy

board2 = copy.copy(board)

It's safe to append/insert/pop/delete/replace items from board2, but not the lists it contains. If you want to modify one of the contianed lists you must create a new list and replace the existing one

row = list(board2[2])
row[3] = 'K'
board2[2] = row

It's a little more work, but a lot more efficient in time and storage

In case of ad is list you can simple call your function as add_at_rank(ad + [], rank).

This will create NEW instance of list every time you call function, that value equivalented of ad.

>>>ad == ad + []
True

>>>ad is ad +[]
False

Pure pythonic :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top