1

I am running into a problem when passing a list to a function. It appears to have a global effect on the list variable, but I did not declare it as global in my function. Can anyone tell me what's happening and how to work around it?

def a_Minus_b(a,b):
    for i in range(len(b)):
        print("a= ", a)
        if b[i] in a:
            a.remove(b[i])
    return a

x = [1,2,3,4]
a_Minus_b(x,x)
a=  [1, 2, 3, 4]
a=  [2, 3, 4]
a=  [2, 4]

Error:

Traceback (most recent call last):
  File "<pyshell#115>", line 1, in <module>
    a_Minus_b(x,x)
  File "<pyshell#112>", line 4, in a_Minus_b
    if b[i] in a:
IndexError: list index out of range
martineau
  • 119,623
  • 25
  • 170
  • 301
XisUnknown
  • 125
  • 1
  • 1
  • 10
  • Please watch: https://www.youtube.com/watch?v=_AEJHKGk9ns, it will be worth the time. – MaxNoe Apr 15 '18 at 23:47
  • 1
    Do not loop by index. `for i in range(len(iterable)): iterable[i]` is an antipattern in python. Just do `for value in iterable` – MaxNoe Apr 15 '18 at 23:50
  • Although I highly recommend the complete video, the part that deals with functions starts at 15:50 – MaxNoe Apr 16 '18 at 00:08
  • Containers, like `list`s, are effectively passed by reference in Python, so your function changes the one it's passed. – martineau Apr 16 '18 at 00:26

1 Answers1

0

Python functions can mutate their arguments, if the argument itself is mutable and python lists are.

If you want to have you function without side effects, copy the data first.

def a_minus_b(a, b):
    a = list(a) # makes a copy and assigns the copy to a new *local* variable
    for val in b:
        print("a = ", a)
        if val in a:
            a.remove(val)
    return a

instead of

a = list(a)

you can use any of:

from copy import copy, deepcopy


a = a[:]         # copies only the references in the list
a = a.copy()     # copies only the references in the list
a = copy(a)      # copies only the references in the list
a = deepcopy(a)  # creates copies also of the items in the list

Also, what you are doing is builtin in python, it is the filter function. It takes an iterable and a function and returns only the elements of the iterable where the function evaluates to True.

print(list(filter(a, lambda elem: elem in b))

filter returns an iterator, to convert it into a list, call list on it.

MaxNoe
  • 14,470
  • 3
  • 41
  • 46