On 21 February 2014 18:00, Peter Otten <__peter__@web.de> wrote:
Oscar Benjamin wrote:
I think we constantly have to deal with libraries that do almost but not exactly what we want them to do. If you look at the code it is clear that the author made a conscious design decision
@property def fieldnames(self): if self._fieldnames is None: try: self._fieldnames = next(self.reader) except StopIteration: pass self.line_num = self.reader.line_num return self._fieldnames
totally unrelated to for loops catching StopIterations.
I was aware of the code. If you look at the commit that made it that way then you can see that the previous implementation was a bare next. It's not clear to me that the behaviour was a design decision or an implementation accident that was propagated for backwards compatibility: $ hg blame Lib/csv.py | grep StopIteration 44735: except StopIteration: $ hg log -p -r 44735 Lib/csv.py <snip> --- a/Lib/csv.py Sat Aug 09 12:47:13 2008 +0000 +++ b/Lib/csv.py Sat Aug 09 19:44:22 2008 +0000 @@ -68,7 +68,7 @@ class DictReader: def __init__(self, f, fieldnames=None, restkey=None, restval=None, dialect="excel", *args, **kwds): - self.fieldnames = fieldnames # list of keys for the dict + self._fieldnames = fieldnames # list of keys for the dict self.restkey = restkey # key to catch long rows self.restval = restval # default value for short rows self.reader = reader(f, dialect, *args, **kwds) @@ -78,11 +78,25 @@ def __iter__(self): return self + @property + def fieldnames(self): + if self._fieldnames is None: + try: + self._fieldnames = next(self.reader) + except StopIteration: + pass + self.line_num = self.reader.line_num + return self._fieldnames + + @fieldnames.setter + def fieldnames(self, value): + self._fieldnames = value + def __next__(self): + if self.line_num == 0: + # Used only for its side effect. + self.fieldnames row = next(self.reader) - if self.fieldnames is None: - self.fieldnames = row - row = next(self.reader) self.line_num = self.reader.line_num Peter wrote:
I fail to see the fundamental difference between next(...) and sequence[...]. There are edge cases you have to consider, and you add comments where you expect them to help your readers to understand your intentions.
The difference is that it's not common practice to catch and ignore IndexError around large blocks of code e.g.: try: do_loads_of_stuff() except IndexError: pass However that is in effect what happens for StopIteration since a typical program will have loads of places where it gets silently caught. The difference is that StopIteration is a particularly innocuous error to leak. Cheers, Oscar