Vishal Sharma
Vishal Sharma

Reputation: 93

having trouble with lists in python

The function satisfiesF() takes a list L of strings as a paramenter. function f takes a string as a parameter returns true or false. Function satisfiesF() modifies L to contain only those strings,s for which f(s) returns true. I have two different programs aimed to produce the same output. But I am getting different outputs.

First program:

def f(s):
    return 'a' in s

def satisfiesF(L):
    k=[]
    for i in L:
        if f(i)==True:
            k.append(i)
    L=k
    print L
    print
    return len(L)


L = ['a', 'b', 'a']
print satisfiesF(L)
print L

Output:

['a', 'a']

2

['a', 'b', 'a']

Second program:

def f(s):
    return 'a' in s


def satisfiesF(L):
    for i in L:
        if f(i)==False:
            L.remove(i)

    print L
    print
    return len(L)

L = ['a', 'b', 'a']
print satisfiesF(L)
print L

output:

['a', 'a']

2

['a', 'a']

Please explain why these are giving differnt outputs.

Upvotes: 1

Views: 1474

Answers (5)

Marcellinov
Marcellinov

Reputation: 301

In the first program, if you want to mutate the original list L and see the change made by your function, you should replace L = K in your code with L[:] = k:

def satisfiesF(L):
    k=[]
    for i in L:
        if f(i)==True:
            k.append(i)

    # new code --------
    L[:] = k   # before: L = K
    # -----------------

    print L
    print
    return len(L)

This will give you ['a', 'a'] outside the function.

About mutating a list within a loop in the second program... Just to remember that during a "for" loop, python keeps track of where it is in the list using an internal counter that is incremented at the end of each iteration.

When the value of the counter reaches the current length of the list, the loop terminates. This means that if you are mutating the list within the loop you can have surprising consequence.

For example, look at the for loop below:

my_list = ['a', 'b', 'c', 'd']

>>>print "my_list - before loop: ", my_list
my_list - before loop:  ['a', 'b', 'c', 'd']


for char in my_list:
    if char == 'a' or char == 'b':
        my_list.remove(char)

>>>print "my_list - after loop: ", my_list    
my_list - after loop:  ['b', 'c', 'd']

Here, the hidden counter starts out at the index 0, discovers that "a" (in my_list[0]) is in the list, and remove it, reducing the length of my_list to 3 (from the initial 4). The counter is then incremented to 1, and the code proceeds to check the "if" condition, at the position my_list[1], in the mutated list (now of len == 3). This means that you will skip the "b" character (present now at the index 0) even if it had to be remove it.

One solution for this is to use slicing to clone and create a new list where you can remove items from it:

cloneList = my_list[:]

Upvotes: 0

JetLag
JetLag

Reputation: 296

In the question, there are two Ls. A global one and a local one. The print L statement prints the GLOBAL L, which you did not mutate in the programme.

Therefore, in order to let the programme knows that you want to mutate the global L, instead of the local L, you can add the line globals()['L'] = L to your first programme. I hope this can help!

Upvotes: 1

Manohar Reddy Poreddy
Manohar Reddy Poreddy

Reputation: 27515

This was down voted mistakenly. The question was changed. So, the answer got outdated. Following is the answer for changed question:

L=k

Above would mean that we lost the reference to L.

So, Try this:
To the 1st program, comment the above assignment, do below, to retain reference to L:

# L=k
del L[:]
L[:] = k

Now both programs will output same, below one:

['a', 'a']

2
['a', 'a']

Best of luck.

Upvotes: 1

Padraic Cunningham
Padraic Cunningham

Reputation: 180542

In your second function you are seeing 2 as the length and all the elements in L outside the function because you are setting a local variableL which is a reference to k, your L created outside the function is not affected. To see the change in L you would need to use L[:] = k, then printing L will give you ['a', 'a'] outside the function as you are changing the original list object L list passed in to the function.

In the first you are directly modifying L so you see the changes in L outside the function.

Also never iterate over a list you are removing element from, if you make L = ['a', 'b', 'a','a','d','e','a'], you will get behaviour you won't expect. Either make a copy for i in L[:] or use reversed for i in reversed(L):

Upvotes: 2

rlbond
rlbond

Reputation: 67847

In the first function, you assign over L in satisfiesF(), but you never modify the original list. When you write L=k, that makes the reference L now refer to the same list as k. It doesn't assign to the original list.

In contrast, in the second function you modify L without reassigning to it.

Also, as a side note, you shouldn't modify a list while you iterate over it.

As a second side note, you can rewrite satisfiesF as a one-liner:

L = [item for item in L if f(item)]

Upvotes: 1

Related Questions