On 2/13/2014 10:28 AM, Terry Reedy wrote:
On 2/13/2014 4:24 AM, Nick Coghlan wrote:
General comment: like Raymond, I'm inclined to favour a nice expression friendly exception handling syntax, precisely because of the proliferation of relatively ad hoc alternative solutions (in particular, the popularity of being able to pass in default values to handle empty iterables).
Leaving aside syntax, the idea makes sense to me as follows:
In Python, 'a or b' is not a just a logic operator but is generalized to mean, for instance, the following: 'a, unless a is falsey, in which case, b instead. The proposal is to introduce a syntax that means the same with 'unless a is falsey' replaced by 'unless a raises an exception of class C'. The class 'C' make 'a new_op b' inadequate.
The two alternations are related when exception C results from an input that is at least conceptually falsey. Some such cases can be handled by 'or' (see below). Iterators, however, are seen as truthy even when empty (conceptually falsey). And that cannot be fixed since some iterators cannot know if they are empty until __next__ is called and bool is not supposed to raise.
One use case, for example, is handing IndexError when retrieving an item from a sequence (which currently has no nice standard spelling, and isn't amenable to the "pass in a default answer" solution because it isn't a normal function call).
This is easy, if not obvious
To be fair, this is only easy for the index 0 case, which is however, common.
(seq or ['default'])[0] 'default'
(Python's precedence rules make the parentheses optional).
Doing something similar instead of dict.get is messier but conceptually the same.
{}.get('key', 'default') 'default' {} or {'key':'default'}['key'] 'default'
However, this is a rare case.
The general scheme is f(a or b, *args), where b is the input to f that gives the default as the output. Sort of inverse(partial(f, args))(default).
This does not work if the exception-raising inputs are not all seen as falsey (as with empty iterables)
or with non-empty lists whose length is less than the index+1 (though this latter example can be 'fixed' by using a conditional expression).
or if one cannot invert the partial function to get b.
Or if it is unboundedly expensive.
(lis if len(lis) > n else (n+1)*['default'])[n] # () needed 'default'
Doing something similar for a non-empty dict that might be missing a key is at least as messy.
In either case, we should or must input a and replace exceptions with b.
-- Terry Jan Reedy