Hacker News new | ask | show | jobs
by nerdponx 996 days ago
Yeah, this one is weird:

  gs1 = (((i, j) for i in "abc") for j in range(3))
  gs2 = [((i, j) for i in "abc") for j in range(3)]

  print(list(map(list, gs1)))
  print(list(map(list, gs2)))
Results:

  [[('a', 0), ('b', 0), ('c', 0)], [('a', 1), ('b', 1), ('c', 1)], [('a', 2), ('b', 2), ('c', 2)]]
  [[('a', 2), ('b', 2), ('c', 2)], [('a', 2), ('b', 2), ('c', 2)], [('a', 2), ('b', 2), ('c', 2)]]
That's a nice "wat" right there. I believe the explanation is that in gs2, the range() is iterated through immediately, so j is always set to 2 before you have a chance to access any of the inner generators. Whereas in gs1 the range() is still being iterated over as you access each inner generator, so when you access the first generator j=1, then j=2, etc.

Equivalents:

  def make_gs1():
      for j in range(2):
          yield ((i, j) for i in "abc")

  def make_gs2():
      gs = []
      for j in range(2):
          gs.append(((i, j) for i in "abc"))
      return gs
Late binding applies in both cases of course, but in the first case it doesn't matter, whereas in the latter case it matters.

I think early binding would produce the same result in both cases.

1 comments

or you could just be eager and use lists:

    >>> [[(i, j) for i in "abc"] for j in range(3)]
    [[('a', 0), ('b', 0), ('c', 0)], [('a', 1), ('b', 1), ('c', 1)], [('a', 2), ('b', 2), ('c', 2)]]
Right, creating generators in a loop is not usually something you want to do, but it's meant to demonstrate the complexity that arises from late binding rather than demonstrate something you would actually want to do in a real program.