6

This is from page 204 of Python for Data Analysis by Wes Mckinney

genre_iter = (set(x.split('|')) for x in movies.genres)
genres = sorted(set.union(*genre_iter))

This code works perfectly when using the %paste method in IPython. The code also works perfectly when run in Python shell. However, when I type the second line into IPython directly, without the %paste method

genres = sorted(set.union(*genre_iter))

I get the following error

TypeError: descriptor 'union' of 'set' object needs an argument

this appears to be a bug, unless there is a feature of IPython that I am still unaware of.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
michael_j_ward
  • 4,369
  • 1
  • 24
  • 25

1 Answers1

14

You have exhausted the generator. Re-define it before using it again:

genre_iter = (set(x.split('|')) for x in movies.genres)
genres = sorted(set.union(*genre_iter))

In python, once you have looped over all the elements of an iterator, you cannot loop over the iterator again (it is now empty).

Because the genre_iter iterator is empty, you are not passing any arguments to set.union() and it thus complains:

>>> set.union()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'union' of 'set' object needs an argument

Just to be explicit: you did not find a bug in ipython. You can reproduce the same issue in a regular python prompt.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • What is the purpose of the `*` before the `genre_iter`? I was trying to figure out a similar code in a GitHub repo. Another Python guide mentioned that it is for unpacking sets for union API. Is that the correct interpretation? – akalanka Jul 22 '22 at 19:17
  • 1
    See [this question](https://stackoverflow.com/q/2921847/100297); it unpacks `genre_iter` into separate arguments for the `set.union()` method. – Martijn Pieters Aug 13 '22 at 11:10