2

I'm learning python and would like to understand what is happening here. I'm sure I'm missing something basic. I'm getting elements from two lists that do not show up in both. Here is my code with output:

    l = [1, 8, 3, 4]
    m = [4, 6, 3, 8]

    Method 1:
    r = list(set(l) - set(m)) + list(set(m) - set(l))
    [1, 6]

    Method 2:
    print [(x,y) for x in l for y in m if x not in m and y not in l]
    [(1, 6)]

    Method 3 (same but returns a list):
    print [[x,y] for x in l for y in m if x not in m and y not in l]
    [[1, 6]]

I would like a list comprehension that would return the same list as returned by method 1.

Also, as far as I understand I'm getting a generator as a result of the code in list comprehension. However, I cannot turn it into a simple list:

    res = ((x,y) for x in l for y in m if x not in m and y not in l)
    print list(res)
    [(1, 6)]

Why is that? I'm expecting:

    [1, 6]

EDIT: my main question is: why can't I turn generator from my list comprehension above into a list? According to the accepted answer in this question using list(res) should work. I want to understand why it doesn't.

cs95
  • 379,657
  • 97
  • 704
  • 746
Lidia
  • 2,005
  • 5
  • 25
  • 32

2 Answers2

2

list(set(l) - set(m)) + list(set(m) - set(l)) is a windy way of finding the symmetric difference between two sets ("the set of elements which are in either of the sets and not in their intersection"—Wikipedia).

>>> set(l).symmetric_difference(m)
{1, 6}

Anyway, with a list comprehension, this is how I'd do it:

>>> {el for (this,other) in [(m,l),(l,m)] for el in this if el not in other}
{1, 6}

Which is the same as

>>> symdif = set()
>>> for this,other in [(m,l),(l,m)]:
...     for el in this:
...          if el not in other:
...              symdif.add(el)
...
>>> symdif
{1, 6}

...not that I'd recommend it.

cs95
  • 379,657
  • 97
  • 704
  • 746
  • 1
    @U9-Forward That's the symmetric difference. The reason I didn't do it is because I didn't want to convert both l AND m into a set to compute it. With the `symmetric_difference`, I don't have to convert both, just one. – cs95 Nov 09 '18 at 03:43
  • @coldspeed: Note: `symmetric_difference` converts to `set` under the hood in the current implementation on CPython, so you end up with the temporary `set` no matter what you do. – ShadowRanger Nov 09 '18 at 03:51
  • @ShadowRanger True, but the function does it for me.... might be just me but I find the function call cleaner... must be my pandas background. :-) – cs95 Nov 09 '18 at 03:52
  • This is what I'm getting as output from symmetric_difference: set([1, 6]) I'm confused, is it a set containing a list of two elements? I'm on python 2.7, if that makes a difference. – Lidia Nov 10 '18 at 02:54
  • @Lidia Convert it to a list by running `list(...)` – cs95 Nov 10 '18 at 02:55
  • @coldspeed Thanks, this returns a list: list(set(l).symmetric_difference(m)) – Lidia Nov 10 '18 at 02:56
  • @Lidia If that's what you wanted, please mark the answer as accepted, if it solved your problem. Thanks. – cs95 Nov 10 '18 at 03:10
  • As noted below, my main question remains: why can't I turn generator from my list comprehension to a list? Also, what kind of data structure is this from your answer: `[(m,l),(l,m)]`? A list of tuples containing lists? Can't wrap my mind around it... – Lidia Nov 10 '18 at 03:12
  • @Lidia There is nothing to debate, your list comprehension is simply incorrect. I see your if condition says `if x not in m and y not in l]` which has nothing to do with the definition of "symmetric difference" which is "((this - other) U (other - this))". How do you represent this as a list comprehension? By considering this = m and other = l first, and then this = l and other = m second, and running the same logic "el for this in el if el not in other". I hope it is clear why your initial attempt is not right. – cs95 Nov 10 '18 at 03:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183403/discussion-between-lidia-and-coldspeed). – Lidia Nov 10 '18 at 03:23
1

It's because your converting the generator to a list!

So it's gonna be equivalent to a list comprehension!!!

Also another option is:

list(set(l)^set(m))

Very short and good.

U13-Forward
  • 69,221
  • 14
  • 89
  • 114
  • 1
    Note: `list(set(l)^set(m))` doesn't preserve the partial ordering based on set of origin that `list(set(l) - set(m)) + list(set(m) - set(l))` does, so it might not be appropriate, but if the ordering isn't important, yeah, it's clearly the best solution. – ShadowRanger Nov 09 '18 at 03:48
  • This is a great solution (without preserving order), however, I still don't understand why I can't convert output of my list comprehension to a simple list. This was my question. The accepted answer in this [question](https://stackoverflow.com/questions/24130745/convert-generator-object-to-list-for-debugging) says you can get a list from generator by doing `list(gen)`, so why isn't it working with my generator? – Lidia Nov 10 '18 at 03:05