Pythonic style

Stavros Macrakis macrakis at alum.mit.edu
Wed Sep 23 17:04:56 EDT 2020


Thanks, Chris, for the useful comments. Comments in line (with snipping of
unnecessary content):


> > some more variants which *don't* use tuple unpacking, on the theory
> that the coding patterns may be useful in
> > other cases where unpacking doesn't apply.
>
> When doesn't it apply? Can you elaborate on this? It might be easier
> to advise on Pythonic style when the specific requirements are known.
>

No specific requirement. These are *finger exercises* intended to isolate
one issue for discussion rather than be useful in themselves.


> > ...but there are ways to avoid the verbose exception handling.
> In Python, exception handling IS the way to do these things.


Good to know. Since *try* statements are at least 4 lines long, I was
trying for something more concise. And maybe somewhere in the back of my
mind, I have the anti-Pythonic principle that exceptions should be reserved
for errors. I'll try to suppress that. I remember one (very old) paper
<http://web.cecs.pdx.edu/~black/publications/Black%20D.%20Phil%20Thesis.pdf>
arguing that exceptions were a bad idea in general... but I have
implemented and used exceptions in many languages, both before and after
that paper!


> > _uniq = []
> > def firstg(iterable):
> >     it = iter(iterable)
> >     val0 = next(it,_uniq)
> >     val1 = next(it,_uniq)
> >     if val0 is not _uniq and val1 is _uniq:
> >         return val0
> >     else:
> >         raise ValueError("first1: arg not exactly 1 long")
> >
> > But I don't know if the *_uniq* technique is considered Pythonic.
>
> It is when it's needed, but a more common way to write this would be
> to have the sentinel be local to the function (since it doesn't need
> to be an argument):
>

What do you mean by an argument?

Is it not considered Pythonic for constants to be calculated once at the
module or class level rather than be recalculated every time a function is
entered?

def firstg_variant(iterable):
>     it = iter(iterable)
>     sentinel = object()
>     first = next(it, sentinel)
>     if first is sentinel:
>         raise ValueError("empty iterable")
>     second = next(it, sentinel)
>     if second is not sentinel:
>         raise ValueError("too many values")
>     return first
>
> But getting a return value and immediately checking it is far better
> spelled "try/except" here. (Note, BTW, that I made a subtle change to
> the logic here: this version doesn't call next() a second time if the
> first one returned the sentinel. This avoids problems with broken
> iterators that raise StopException and then keep going.)
>

Yes, I hadn't thought of that. I was trying to avoid repeating the *raise*
 statement.

Thanks again for the helpful comments. They give me a better idea of good
Python style.

             -s


More information about the Python-list mailing list