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 -- https://mail.python.org/mailman/listinfo/python-list