keeping state in an iterator object by rebinding next()
Terry Reedy
tjreedy at udel.edu
Wed Mar 19 18:56:46 EDT 2008
"Wilbert Berendsen" <wbsoft at xs4all.nl> wrote in message
news:200803192036.22626.wbsoft at xs4all.nl...
| Hi,
|
| i am writing a simple parser, that generates tokens. The parser needs to
| maintain some state, because some parts of the file consist of different
| tokens. I thought the object could simply remember its state by assigning
| it's next() method to the method that is currently parsing. When the
state
| changes, the called method rebinds next() and the next token will be
returned
| by that function. Here's an example, proving that this indeed works.
For some undisclosed version.
| >>> class A:
I stronly suspect that if you make this a new-style class by adding
'(object)',
this will not work. See below.
| ... def a(self):
| ... self.next = self.b
This attaches the class attribute (method) b to the instance
| ... return 1
| ... def b(self):
| ... self.next = self.a
| ... return 2
| ... def __iter__(self):
| ... return self
Since A does not have a 'next' (or __next__, in 3.0), instances of A are
not really iterators, under the new iterator protocol, and hence __iter__
above is not valid. See below.
| ...
| >>> a=A()
| >>> a.a()
| 1
| >>> a.next()
| 2
| >>> a.next()
| 1
| >>> j=0
| >>> for i in a:
In 3.0a3, which uses new-style classes and iterator protocol, this croaks
with
TypeError: iter() returned non-iterator of type 'A'
| ... j += 1
| ... if j > 10: break # prevent from running endlessly
| ... print i
I suspect that this only works because the for-loop, not finding A.next,
used the old iterate-by-index protocol and calls a.next with 0, 1, 2, ....
To find out, put 'print self' inside methods a and b.
...
| 2
| 1
| 2
| 1
| 2
| 1
| 2
| 1
| 2
| 1
| >>>
|
| my question is: is this legal Python?
In whatever version you used, it seems to be, but not in the future.
| An iterator could save the next() method object,
You mean, the iterator user.
| and in that case it could stop working.... It works now, because
| apparently the for- construct resolves 'next' each time for the object
before
| calling it.
A standard idiom for explicit iteration with a new style iterator and
'while' would do just what you suggest.
itnext = iter(a).next
try:
while True
print itnext()
except StopIteration:
pass
| The other solution would be just jumping to the correct method from
within the | next() method. But that gives an extra call...
Or, make a and b staticmethods, remove the self param, and make __iter__ a
generator
def __iter__(self):
while True:
yield self.next()
The speed penalty of the indirection thru the generator is minimal.
Terry Jan Reedy
More information about the Python-list
mailing list