Another way that generators are not lists: modifying them
A long time ago, I wrote some stuff on how generators are not lists (okay, technically it was about iterators), and one of the things that I mentioned is that generators do not have list methods. Well, there's a consequence of that that only struck me recently: you need completely different code to modify a returned generator than to modify a returned list.
Suppose you have a function that returns something that is conceptually a list of items. Further suppose that you have another function that modifies what the first function returns; perhaps you want to add something on the end. If you know you're dealing with a list, you write:
def append(func, extra): r = func() r.extend(extra) return r
If func()
is a generator, this code blows up. You have two choices;
first, you can forcefully turn the result of func()
into a list, and
second, you can rewrite append()
as a generator (which will work
regardless of what func()
returns, but may have consequences that
make it undesirable):
def append(func, extra): for it in (func(), extra): for e in it: yield e
(Yes, yes, one can write this using
itertools.chain()
. Then people would have to look it up.)
In either case, you have to actively make a decision about what your
function will do. You cannot passively modify whatever you get handed
and pass it up to your caller without changing its nature; you must
decide that no matter what func()
returns, you're either returning a
list or an iterator.
(Technically you can, since you can see if you got handed something that follows the iterator protocol or whether it looks sequence-like. But that way lies madness.)
|
|