-- ========================== Jeffrey E. McAninch, PhD Physicist, X-2-IFD Los Alamos National Laboratory Phone: 505-667-0374 Email: mcan...@lanl.gov ==========================
_______________________________________________
Python-ideas mailing list
Python...@python.org
http://mail.python.org/mailman/listinfo/python-ideas
I fear being able to "express" too much. That is, we're going to
remove all that indentation gives us if eventually everything is given
an expression form.
Let's add an import expression, next. Maybe this?
sys.exit(1) from sys
Joke, obviously.
> _______________________________________________
> Python-ideas mailing list
> Python...@python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>
>
--
Read my blog! I depend on your acceptance of my opinion! I am interesting!
http://techblog.ironfroggy.com/
Follow me if you're into that sort of thing: http://www.twitter.com/ironfroggy
> _*Very Simple Example - type coercion:
> *_Current approach:
> try:
> x = float(string)
> except:
> x = float('nan')
>
> Proposed solution using exception-based conditional expression:
> x = float(string) except ValueError: float('nan')
-1
I really don't like the colon in the middle of the expression. It
just looks like broken syntax because everywhere else in Python colon
introduces a suite. I wonder if you really need it?
I generally don't like a general conditional expression. For reasons
presented below, I don't think it's as flexible as think it can be.
OTOH, I fund it much less readable than the existing statement-based
construction.
> _*Simple Example - type coercion in a list comprehension:
> *_Current approach:
> def safe_float(string):
> try:
> x = float(string)
> except ValueError:
> x = float('nan')
> return x
> ...
> xs = (safe(float(string)) for string in strings)
I don't have a problem with the above. But I can see that for
> In data processing, I often string together a sequence of iterable
> list comprehensions, corresponding to a sequence of operations on a
> given dataset "ys" to produce a processed dataset "x":
an exception-handling clause in comprehensions (and generator
expressions?) might be useful, to make the exceptions handled
explicit. For the cases I can imagine using myself, I would generally
prefer the approach of defining functions to handle the exceptions,
because they'd all be similar (eg, coercing exceptions to float("nan")
as above), though.
> try-except syntax. So the following examples would be allowed:
> x = expression0 except: default_expression
> x = expression0 except exception1: expression1 except exception2: expression2 except: default_expression
But it seems to me the ez-read interpretation of
x = expression0 except exception1: expression1 except: default_expression
is
x = expression0 except exception1: (expression1 except: default_expression)
Ie, your parser resolves the "dangling except" ambiguity in the
opposite way to the conventional resolution of the "dangling else"
ambiguity. And what if excepts are mixed with conditional
expressions?
x = exp0 except exc1: exp1 if exp2 else exp3 except: exp4
Does the bare except bind to exp0, the if expression, or exp3? I'm
sure you can define rules to disambiguate. However, I suspect that it
will be hard to get agreement that any given set of rules is the
appropriate way to resolve the ambiguity.
This is a pretty good idea. I can definitely see where I would use it
in my own code. The only thing I would caution is in your simple
example. I would choose to create a module full of conversion
functions. It violates DRY to have the 'try float otherwise Nan'
statement all over the place.
--
David
blog: http://www.traceback.org
twitter: http://twitter.com/dstanek
Following up on my previous post, I think the general syntax would be
something like:
exception_expression :== nominal_value {except exception_tuple
exception_value}* {except default_value}
Hopefully this disambiguates the issue, at least from the parser point
of view. Requiring an explicit tuple of exceptions (ie., requiring the
parens around the tuple of exceptions) makes sense too, and I thought I
had seen other discussions of requiring explicit tuples where currently
parens could be implied.
With the definition above, I think the combinations of exception
expressions with if-else conditional expressions is probably also
unambiguous (though not necessarily easily read). I haven't sat down to
verify this mathematically yet though.
Allowing parens around the except clauses might help to make it more
readable?
x = float(string) (except (ValueError,) float('nan')) (except
(SomeOtherException,) someOtherValue)
Okay, maybe not?
(BTW: thanks for the cool new verb -- disambiguate -- though my friends
and family may not be so happy when I start using it at every opportunity!)
--
==========================
Jeffrey E. McAninch, PhD
Physicist, X-2-IFD
Los Alamos National Laboratory
Phone: 505-667-0374
Email: mcan...@lanl.gov
==========================
_______________________________________________
A much better idea would be to find a way to make all compound
statements into expressions, future-proofing the decision and avoiding
the redundancy between "compound statement statement" and "compound
statement expression".
Personally, I think the above is likely to be the approach in a serious
application. For lightweight applications, an expression-based solution
would be acceptable, but it has the disadvantage of not being able to
be documented or tested independently.
> Proposed solution using exception-based conditional expression:
> xs = ((float(string) except ValueError: float('nan')) for string
> in strings)
Others have suggested that the colon should be dropped. I dislike that
idea, because there's nothing but whitespace delimiting the list of
exceptions from the except-expression:
EXPR except EXCEPT-LIST EXCEPT-EXPR
As far as I can tell, that would be unprecedented in Python syntax.
...
> Here is a slightly more involved example than the examples presented
> above. In data processing, I often string together a sequence of
> iterable list comprehensions, corresponding to a sequence of
> operations on a given dataset "ys" to produce a processed dataset
> "x":
> xs = (operation_A(x) for x in ys)
> xs = (operation_B(x) for x in xs if filter_B(x))
> xs = (operation_C(x) if (some_condition(x)) else operation_D(x)
> for x in xs if filter_C(x))
> # final, explicit list of values
> xs = [ x for x in xs ]
Ouch! That last one is better written as:
xs = list(xs)
> This is often a flexible way for me to define processing and
> filtering sequences which also seems
> to have good performance on very large datasets.
Chaining iterators together is a powerful technique, and you don't have
to be limited to generator expressions, you can use regular generators
too:
def operate(xs):
for x in xs:
try: yield operation_C(x)
except ValueError: yield operation_D(x)
xs = (operation_A(x) for x in ys)
xs = (operation_B(x) for x in xs if filter_B(x))
xs = operate(xs)
# finally convert to a list
xs = list(xs)
> One advantage is
> that I can quickly mix-and-match from existing processes like this to
> make a new process. An exception-based conditional would go nicely
> into many of these process sequences, keeping them both robust and
> flexible. xs = (operation_N(x) except exceptionN: operation_Nprime(x)
> for x in xs)
So far this is an argument for iterator-based processing, not for
exception-based conditionals :)
You can use exceptions in generator exceptions, you just have to put
them into a function first.
> I also often have object classes which have some common method or
> attribute. For instance, some of my objects have scope-dependent
> values: x = y.evaluate(scope))
> where scope is typically locals(), globals(), or some other
> dictionary-like container. But, to keep my code modular, I want to
> handle, in the same lines of code, objects which do not have some
> particular method, which leads me to lines of code like:
> x = y.evaluate(locals()) if ('evaluate' in y.__dict__) else y
Is that real code? I don't think it works the way you think. Given a
reasonable definition for y (an instance of a class with no special
tricks) the if test will never succeed:
>>> class MyObject(object):
... def evaluate(self, arg):
... return arg
...
>>> y = MyObject()
>>> y.evaluate(42)
42
>>> 'evaluate' in y.__dict__
False
A better way of writing that would be:
x = y.evaluate(locals()) if hasattr(y, 'evaluate') else y
which have the bonus of working correctly even if y gets its evaluate()
method via inheritance.
Another way, two lines instead of one:
try: x = y.evaluate(locals())
except AttributeError: x = y
A third method: make sure all objects you pass have an evaluate()
method, even if they are a null-operation that just return self.
> This seems not very "Pythonic", similar to using type-testing instead
> of try-except.
You're talking about the Look Before You Leap technique rather than
Easier To Ask Forgiveness Than Permission.
Both techniques are valid, and both have advantages. ETAFTP is
advantageous when exceptions are rare and hard to predict; LBYL is
better if exceptions are common and easy to predict.
> In the thread of my previous post to py-dev, there were comments,
> questions, and suggestions concerning the details of the syntax.
> Having reflected on this for a couple weeks, I am now most strongly
> supportive of what is essentially just an inline compression of the
> current try-except syntax. So the following examples would be
> allowed:
> x = expression0 except: default_expression
I would argue strongly that allowing a bare except is a SERIOUS mistake.
Bare excepts have very few legitimate uses, and now that string
exceptions are deprecated and very rare, it's no hardship to write:
except Exception:
if you want to catch everything except KeyboardInterrupt and one or two
other things, or:
except BaseException:
for those rare times you actually do want to catch everything.
--
Steven D'Aprano
for ==> list comprehension, iterator expression
while ==> list comprehension, iterator
if ==> if-else conditional expression
def ==> already have lambda expressions
try ==> my proposal
class ==> Okay, so this one doesn't have a compound statement,
but where would you use it?
with ==> I haven't had an occasion to use with,
so can't think of an example where it could
be useful as a compound expression.
Seems to me try-except is kind of sticking out here, and I've
tried to show what I think are natural and common places where
a try-except expression would be appropriate.
--
==========================
Jeffrey E. McAninch, PhD
Physicist, X-2-IFD
Los Alamos National Laboratory
Phone: 505-667-0374
Email: mcan...@lanl.gov
==========================
_______________________________________________
> Python's list of compound statements is pretty thin (just 7),
> and 4 of these already have expression forms.
>
> for ==> list comprehension, iterator expression
> while ==> list comprehension, iterator
> if ==> if-else conditional expression
But that one is ugly (that is, I don't think anyone *likes* the syntax,
it's just considered the least worst), and some of us consider that its
usefulness is dubious, or at least weak. I think the main reason that
Python has a ternary if statement is to stop people writing the
error-prone:
cond and x or y
as a short-circuit ternary if.
> def ==> already have lambda expressions
>
> try ==> my proposal
>
> class ==> Okay, so this one doesn't have a compound statement,
> but where would you use it?
But it does have a function:
>>> type("MyClass", (object,), {})
<class '__main__.MyClass'>
Or rather, class is syntactic sugar for the type known as type(), which
isn't actually a function, but you call it as if it were one.
> with ==> I haven't had an occasion to use with,
> so can't think of an example where it could
> be useful as a compound expression.
>
> Seems to me try-except is kind of sticking out here, and I've
> tried to show what I think are natural and common places where
> a try-except expression would be appropriate.
At the moment, I would vote:
-0 for the general idea
-1 if it allows a bare except
I could possibly be convinced to change my first vote to +0 if I could
see some real examples of code where this would be useful. (However, I
don't think there's any power in the universe which could convince me
that allowing bare excepts here was a good thing :)
Has anyone considered binding to an exception instance? E.g:
x = f(x) except (ValueError, TypeError) as e: extract_from(e)
--
Steven D'Aprano
> Others have suggested that the colon should be dropped. I dislike that
> idea, because there's nothing but whitespace delimiting the list of
> exceptions from the except-expression:
>
> EXPR except EXCEPT-LIST EXCEPT-EXPR
I agree. Even if it can be parsed unambiguously, it's
going to seem weird and confusing to a human.
So far I haven't seen anything I like better than
<expr> except <value> if <exception>
despite the fact that it uses the words 'except'
and 'if' in a different way than elsewhere.
--
Greg
Many people have tried, and dismally failed to convince
Guido that their suggested solution would be of benefit
to the language.
--
Greg
That's actually a good point - eliminating the persistent use of buggy
workarounds was definitely one of the major motivations behind accepting
PEP 308.
In this case, there *are* no buggy workarounds: if you want to trap
exception, you have to use a real statement.
Without that additional motivation of bug prevention, I really doubt
this proposal is going to go anywhere.
Cheers,
Nick.
--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia
---------------------------------------------------------------
But I don't think I saw any reasonable (= readable) syntax.
Anything with bare semicolon is clearly confusing because bare
semicolon has a well-defined meaning as ending a line and starting new
block.
Anything with bare except clause isn't good because catching all
exceptions is a wrong style.
Anything that encourages the use of \ line continuation character
clearly contradicts the well-established guideline that this character
should be used as a last resort.
-1 on either of above suggestions.
But there are deeper problems even in the "simple example". To see
them, let me show how this example should look in programming style I
strive for:
def safe_float(string: 'any string', invalid = float('nan')) -> 'float':
'''Wrapper around float() that converts all invalid values to given param.
Use with care --- this will also catch invalid inputs and other cases
where notifying the user with exception is a better idea.
'''
try:
return float(string)
except ValueError:
return invalid
...
class tests:
def testSafeFloat(self):
assert safe_float('5.7') == float('5.7')
assert safe_float('invalid') == float('nan')
...
# Here string comes from an experiment and I know it's either empty or should
# be valid.
xs1 = (float(string or 'nan') for string in strings)
# Here string comes from a user input. Tell him if he types something wrong.
for string in strings:
try:
xs2[i] = float(string)
catch ValueError:
string = input('Please retype string #{}'.format(i))
...
# Here strings are a result of series of experiments. Unfortunately we don't
# know what format the researchers used for 'no data' or 'invalid data'. So
# for now we'll just change all of these values to 'nan'.
xs3 = (safe_float(string) for string in strings)
Your proposed solution using exception-based conditional expression:
# Here strings are a result of series of experiments. Unfortunately we don't
# know what format the researchers used for 'no data' or 'invalid data'. So
# for now we'll just change all of these values to 'nan'.
# But, unfortunately, if in the future we'll decide to use some different
# function to process data, we'll have to rewrite this whole cycle...
xs = ((float(string) except ValueError: float('nan')) for string in strings)
I think from my examples you get what I'm trying to say -- when you're
thinking about easily catching exceptions, you're either ok with something
simpler or you're actually trying to apply a function. So why not do it
explicitely?
Ilya.
Greg Ewing wrote:
> Steven D'Aprano wrote:
>
>> Others have suggested that the colon should be dropped. I dislike that
>> idea, because there's nothing but whitespace delimiting the list of
>> exceptions from the except-expression:
>>
>> EXPR except EXCEPT-LIST EXCEPT-EXPR
>
> I agree. Even if it can be parsed unambiguously, it's
> going to seem weird and confusing to a human.
>
> So far I haven't seen anything I like better than
>
> <expr> except <value> if <exception>
>
> despite the fact that it uses the words 'except'
> and 'if' in a different way than elsewhere.
Possibly the only way to do this within those constraints is a purely
syntactic.
value = (expr1 :exception: expr2)
Sense most people (and editors) recognize exceptions names easily, there
really isn't a strong need to use the key word "except" in an expression
like this.
The colons in this case can be considered to be similar to slice syntax.
And if the colons are still considered bad, possibly some other symbol
could be used.
Ron
Which constraints? Are you referring to the fact that any proposed
syntax must be capable of being unambiguously parsed?
> is a purely syntactic.
I'm not sure I understand. Can you give an example of a proposal which
*isn't* syntactic?
> value = (expr1 :exception: expr2)
Are the brackets intended to be part of the syntax, or are they there
just for grouping? In other words, would this be allowed?
value = function(expr1 :exception: expr2)
Or would we have to write this?
value = function((expr1 :exception: expr2))
> Sense most people (and editors) recognize exceptions names easily,
> there really isn't a strong need to use the key word "except" in an
> expression like this.
The word "except" cues in the reader that they are seeing an
exception-expression, rather than some sort of data structure like
{key:value} or (a, tuple).
For the same reason, list comprehensions use "for":
[x+1 for x in alist]
rather than mathematical notation:
{x+1 ∀ x ∈ alist}
(if that doesn't display for you, the first odd character is an upside
down A, "for all", and the second is a curved E, "element of"). If you
don't like unicode-based source code, we could replace the "for all"
and "element of" symbols with colons:
[x+1 : x : alist]
but we don't because the words "for" and "in" give the reader hints as
to what is being done, which a colon would not.
Words are just symbols, so anytime you have syntax including a keyword,
you could replace the keyword with a symbol. That way leads to APL. As
a general rule, keywords are more readable, punctuation is more
concise.
--
Steven D'Aprano
xs = apply(operation_N, {exceptionN: operation_Nprime}, xs)
which also happens to be shorter than
xs = (operation_N(x) except exceptionN: operation_Nprime(x) for x in xs)
Similarly, multiline try/except as in your last example
x = expression0\
except exception1: expression1\
except exception2: expression2\
...
except exceptionI: expressionI\
...
except: default_expression
is certainly useful. But we're lucky, nearly identical syntax already
exists out of the box!
try: _ = expression0
except exception1: _ = expression1
except exception2: _ = expression2
...
except exceptionI: _ = expressionI
...
except: _ = default_expression
x = _
What's so good about the your snippet that is bad about mine? Anyway,
instead of either of those I would prefer
# In most cases expression0 returns well-formed foo.
try: _ = expression0
except exception1: # Take care of the case of bar being not frobnicable.
_ = expression1
except exception2:
# This looks like magic, but is isn't. See documentation for
# exception2 at http:// some_url for explanation.
_ = expression2
...
except exceptionI: _ = expressionI
...
except exceptionLast:
_ = expressionLast
# All cases I could think of are exhausted! Of course, anything can
# happen so any other exception will propagate up.
x = _
On Thu, Aug 20, 2009 at 3:07 AM, Jeff McAninch<mcan...@lanl.gov> wrote:
Steven D'Aprano wrote:
> On Sat, 22 Aug 2009 11:28:04 am Ron Adam wrote:
>> Greg Ewing wrote:
>>> Steven D'Aprano wrote:
>>>> Others have suggested that the colon should be dropped. I dislike
>>>> that idea, because there's nothing but whitespace delimiting the
>>>> list of exceptions from the except-expression:
>>>>
>>>> EXPR except EXCEPT-LIST EXCEPT-EXPR
>>> I agree. Even if it can be parsed unambiguously, it's
>>> going to seem weird and confusing to a human.
>>>
>>> So far I haven't seen anything I like better than
>>>
>>> <expr> except <value> if <exception>
>>>
>>> despite the fact that it uses the words 'except'
>>> and 'if' in a different way than elsewhere.
>> Possibly the only way to do this within those constraints
>
> Which constraints? Are you referring to the fact that any proposed
> syntax must be capable of being unambiguously parsed?
And also to not using 'except' and 'if' in a different way than elsewhere.
>> is a purely syntactic.
>
> I'm not sure I understand. Can you give an example of a proposal which
> *isn't* syntactic?
Technically you are correct, but I was trying to refer to patterns that do
not use keywords to identify the operation. For example slice operations
use a pattern rather than a unique symbol to identify the operation.
So this is an example of a pattern that could work.
>> value = (expr1 :exception: expr2)
> Are the brackets intended to be part of the syntax, or are they there
> just for grouping? In other words, would this be allowed?
Yes, I intended the brackets to be part of the pattern.
> value = function(expr1 :exception: expr2)
>
> Or would we have to write this?
>
> value = function((expr1 :exception: expr2))
I supose it could work like generator expressions do. Then both of these
could be equivalent.
>> Sense most people (and editors) recognize exceptions names easily,
>> there really isn't a strong need to use the key word "except" in an
>> expression like this.
>
> The word "except" cues in the reader that they are seeing an
> exception-expression, rather than some sort of data structure like
> {key:value} or (a, tuple).
True, it needs to be unique enough to be easily identifiable or it would
not be worth it.
> For the same reason, list comprehensions use "for":
>
> [x+1 for x in alist]
>
> rather than mathematical notation:
>
> {x+1 ∀ x ∈ alist}
It displayed fine. :-)
> (if that doesn't display for you, the first odd character is an upside
> down A, "for all", and the second is a curved E, "element of"). If you
> don't like unicode-based source code, we could replace the "for all"
> and "element of" symbols with colons:
>
> [x+1 : x : alist]
I think having the square brackets with the colons would be a bit too close
to slice notation here. ie... it's not unique enough to be easily
identifiable.
> but we don't because the words "for" and "in" give the reader hints as
> to what is being done, which a colon would not.
This is especially true for more complex operations such as looping and
repeating.
> Words are just symbols, so anytime you have syntax including a keyword,
> you could replace the keyword with a symbol. That way leads to APL. As
> a general rule, keywords are more readable, punctuation is more
> concise.
Yes, I agree, but python isn't that strict about everything. To me a well
defined exception expression that
All the previous suggestions view the term as needing three values, so you
need two separators along with unique symbols (or keywords) to come up with
something that fits the need in a clear and concise way. I was thinking
what if we could use the exception object itself as an operator, then the
term becomes even simpler and more concise. But the only way I can think
to do that in a clear way is to use syntax to identify the exception, such
as putting colons, or some other symbol around the exception. <shrug>
Ron
Ron Adam wrote:
>
>
> Steven D'Aprano wrote:
>> Words are just symbols, so anytime you have syntax including a
>> keyword, you could replace the keyword with a symbol. That way leads
>> to APL. As a general rule, keywords are more readable, punctuation is
>> more concise.
>
> Yes, I agree, but python isn't that strict about everything. To me a
> well defined exception expression that
Editing fragment... Disregard the incomplete sentence.
> All the previous suggestions view the term as needing three values, so
> you need two separators along with unique symbols (or keywords) to come
> up with something that fits the need in a clear and concise way. I was
> thinking what if we could use the exception object itself as an
> operator, then the term becomes even simpler and more concise. But the
> only way I can think to do that in a clear way is to use syntax to
> identify the exception, such as putting colons, or some other symbol
> around the exception. <shrug>
An additional note, the when you view an exception object in the console it
is bracketed by the less than and and greater than signs. Maybe there is a
way to use those in a manner that is consistent with the string repr() returns.
> An additional note, the when you view an exception object in the console it
> is bracketed by the less than and and greater than signs. Maybe there is a
> way to use those in a manner that is consistent with the string repr()
> returns.
There won't be unless you rule out using "x > y" as an expression that
gives the default value. But doing that will make Python's grammar way
too complicated, and you're still left with the problem of "x, y = a <
b, b > c" which is legal today.
It's my understanding that Ruby has run into a lot of ambiguity with
its use of | as both a bitwise operator and a part of a block
definition. This seems worse.
Lots of things have a repr() which includes < and > signs, including
repr() itself.
>>> print repr
<built-in function repr>
Also new style classes and instances, types, functions, methods, etc.
Exceptions are printed with < > signs because exceptions are subclasses
of object. There's nothing specifically "exception-like" about the use
of < and > in the repr of exceptions.
--
Steven D'Aprano