1

When I use one-line-for loop in Python, I will got two different values depending on the process I choose(Assign and append processes). I want to understand the difference and how they work. In the following two examples, I trying to write a program that returns a list that contains only the elements that are common between the lists (without duplicates).

First program with append and I get the right result:

a = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

def common_in_two_lists(list1, list2):
   re_list = []
   [re_list.append(val1) for val1 in list1 for val2 in list2 if val1==val2 and val1 not in re_list]
   return re_list

After call the function and print output list:

l = common_in_two_lists(a, b)
print(l)

and the output is:

[1, 2, 3, 5, 8, 13]

But when I use assign way as the following I will got the wrong answer:

def common_in_two_lists(list1, list2):
   re_list = []
   re_list = [val1 for val1 in list1 for val2 in list2 if val1==val2 and val1 not in re_list]
   return re_list

l = common_in_two_lists(a, b)
print(l)

and the output is:

[1, 1, 2, 3, 5, 8, 13]

Anyone can learn me to understand How do this two different way work?

Joseph Sh
  • 51
  • 4
  • 2
    Abusing list comprehensions for a side effect is a big no-no (https://stackoverflow.com/questions/5753597/is-it-pythonic-to-use-list-comprehensions-for-just-side-effects). Also, in your second example, `re_list = []` is not necessary since the next line creates a new list any way. `re_list = [val1 for val1 in list1 for val2 in list2 if val1 == val2]` will have the exact same output. – DeepSpace Aug 30 '18 at 08:36
  • In the first case you alter the list with each loop. When you reach the second `1` there is already a `1` in `re_list`. – Matthias Aug 30 '18 at 08:40
  • 1
    You should probably study this: https://nedbatchelder.com/text/names.html – PM 2Ring Aug 30 '18 at 08:41
  • 6
    *I trying to write a program that returns a list that contains only the elements that are common between the lists (without duplicates)*... you should take a look at `set`s... eg: `list(set(a).intersection(b))`... – Jon Clements Aug 30 '18 at 08:42
  • @DeepSpace In second example, if I remove the line: `re_list = []` I will get NameError. – Joseph Sh Aug 30 '18 at 08:46
  • 1
    @JonClements This should be the correct (and most elegant) answer – Maor Refaeli Aug 30 '18 at 08:47
  • @JosephSh Becuase you should also delete the `if` condition like I showed – DeepSpace Aug 30 '18 at 08:47

3 Answers3

3

You need to break down the code into simpler form to understand it. Taking the first example.

a = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]


def common_in_two_lists1(list1, list2):
    re_list = []
    for val1 in list1:
        for val2 in list2:
            if val1 == val2 and val1 not in re_list:
                re_list.append(val1)
                print(re_list)
    return re_list


l = common_in_two_lists1(a, b)

OUTPUT

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 5]
[1, 2, 3, 5, 8]
[1, 2, 3, 5, 8, 13]

You can see that the re_list is appending the value every time. And the [] you have put for the first example is useless.

Coming to your second example. If you check the type of the expression, you would find it to be generator.

re_list = print(type(val1 for val1 in list1 for val2 in list2 if val1 == val2 and val1 not in re_list))
<class 'generator'>

And generator expression is evaluated only when you are try to fetch the value, that explains why you are getting the duplicate 1 on this code. Because in the expression re_list is empty when the generator is created.

Finally coming to your solution:

I trying to write a program that returns a list that contains only the elements that are common between the lists (without duplicates).

You should follow Jon Clements♦ advice and implement it using a set

you should take a look at sets... eg: list(set(a).intersection(b))

Arghya Saha
  • 5,599
  • 4
  • 26
  • 48
  • But I still get NameError if remove the useless `re_list = []` from my first example, Why? – Joseph Sh Aug 30 '18 at 09:01
  • @JosephSh `re_list = []` is not useless, it is very much required as it is required for declaration. You cannot append on a list until you have it declared. – Arghya Saha Aug 30 '18 at 09:05
  • `And the [] you have put for the first example is useless.` What I meant by this is the one-liner is having `[]` this become important for the syntax as you are using list comprehension syntax, but overall it is pretty much useless. And you should try to write it in a broken format as what I have done. – Arghya Saha Aug 30 '18 at 09:09
2

In your second example re_list is empty by the time new list is created, so val1 not in re_list is always false. In the first one you create a list of what is returned by re_list.append() (None, as I remember) and assign it to nowhere while modifying re_list.

By the way, why don't you use set() to obtain a list of unique elements?

Sianur
  • 639
  • 6
  • 13
1
def common_in_two_lists(list1, list2):
   re_list = []
   [re_list.append(val1) for val1 in list1 for val2 in list2 if val1==val2 and val1 not in re_list]
   return re_list

is equivalent to:

def common_in_two_lists(list1, list2):
   re_list = []
   for val1 in list1:
       for val2 in list2:
          if val1==val2 and val1 not in re_list:
             re_list.append(val1)
   return re_list

while the second method:

def common_in_two_lists(list1, list2):
   re_list = []
   re_list = [val1 for val1 in list1 for val2 in list2 if val1==val2 and val1 not in re_list]
   return re_list

is equivalent to

def common_in_two_lists(list1, list2):
    re_list_old = []
    re_list = []
    for val1 in list1:
        for val2 in list2:
            if val1==val2 and val1 not in re_list_old: #note re_list_old here
                re_list.append(val1)
    return re_list

It is worth to notice that with the second method you are not checking for duplicates in the final list because everytime you check the empty list while doing val1 not in re_list

Both ways work in O(n**2) time complexity, using sets :

l = list(set(a) & set(b))

it's more efficient and simpler as you can do it with average time complexity O(min(len(a), len(b)) (the worst case is O(len(a)*len(b))).

abc
  • 11,579
  • 2
  • 26
  • 51