[Python-ideas] Function to return first(or last) true value from list

Oscar Benjamin oscar.j.benjamin at gmail.com
Sat Feb 22 15:49:21 CET 2014


On 21 February 2014 18:00, Peter Otten <__peter__ at 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


More information about the Python-ideas mailing list