On 2012-10-25, Terry Reedy <tjre...@udel.edu> wrote:
> The op wondered if these proposals have been made before. They have > been, and have been rejected. Some of the discussion has been on > python-ideas list. But go ahead and brainstorm and discuss.
> Keep in mind that any new syntax has to be a substantial improvement in > some sense or make something new possible. There was no new syntax in > 3.2 and very little in 3.3.
I think the new syntax should be introduced in 2.00. There were a
number of other big changes between 1.52 and 2.00, so that seems like
a good spot to put this change.
-- Grant Edwards grant.b.edwards Yow! !! I am having fun!!!
at gmail.com
> I guess that depends on what sort of programs you write. In my
> experience, EXPR is usually a read from a file/socket/pipe that
> returns '' on EOF. If VAR is not '', then you process, then you
> process it inside the loop.
Right. The same as in
if regex.search(string) as match:
process it
But with
def if_true(expr):
if expr: yield expr
you can do
for match in if_true(regex.search(string)):
process it
But the proposed if ... as ...: statment woulkd be more beautiful by far.
On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
<nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa...@spamschutz.glglgl.de>
wrote:
>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>> selected)
> This generator never ends. If it meets a non-matching value, it just skips
> it and goes on.
next() only returns one value. After it is returned, the generator is
discarded, whether it has ended or not. If there were no valid values
for randrange to select, then it would descend into an infinite loop.
But then, so would the dropwhile and the original while loop.
>> Keep in mind that any new syntax has to be a substantial improvement in
>> some sense or make something new possible. There was no new syntax in
>> 3.2 and very little in 3.3.
> I would consinder this at least as new substantial than
> yield_from it
> as opposed to
> for i in it: yield i
> - although I think that was a good idea as well.
Except that those two are not exactly identical, because "yield from"
also properly delegates sent data and exceptions to the sub-generator.
The actual equivalent code for "yield from expr()", as given in the
PEP, is 39 lines long. This is a substantial feature, not just a
little syntactic sugar.
On Thu, Oct 25, 2012 at 10:36 AM, Ian Kelly <ian.g.ke...@gmail.com> wrote:
> On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
> <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa...@spamschutz.glglgl.de>
> wrote:
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>>> selected)
>> This generator never ends. If it meets a non-matching value, it just skips
>> it and goes on.
> next() only returns one value. After it is returned, the generator is
> discarded, whether it has ended or not. If there were no valid values
> for randrange to select, then it would descend into an infinite loop.
> But then, so would the dropwhile and the original while loop.
To demonstrate that the code does in fact return:
>>> selected = set(range(5))
>>> n = 10
>>> from functools import partial
>>> from random import randrange
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in selected)
>>> j
> On Thu, 25 Oct 2012 11:52:31 +0200, Thomas Rachel wrote:
>> Am 25.10.2012 06:50 schrieb Terry Reedy:
>>> Keep in mind that any new syntax has to be a substantial improvement in
>>> some sense or make something new possible. There was no new syntax in
>>> 3.2 and very little in 3.3.
>> I would consinder this at least as new substantial than
>> yield_from it
>> as opposed to
>> for i in it: yield i
>> - although I think that was a good idea as well.
> Then I think you have misunderstood the purpose of "yield from". The fact
> that you can replace the two lines:
> for value in another_iterator:
> yield iterator
> with a one-liner "yield from another_iterator" is the least important use-
> case for yield-from. If that was the only use-case, it probably would not
> have been allowed, because it adds complication to the language for a
> trivial gain.
> The purpose of yield-from is to transfer control to another coroutine,
> not to save one trivial line of code.
> [quote]
> However, if the subgenerator is to interact properly with the caller in
> the case of calls to send(), throw() and close(), things become
> considerably more difficult. As will be seen later, the necessary code is
> very complicated, and it is tricky to handle all the corner cases
> correctly.
> A new syntax will be proposed to address this issue. In the simplest use
> cases, it will be equivalent to the above for-loop, but it will also
> handle the full range of generator behaviour, and allow generator code to
> be refactored in a simple and straightforward way.
> [end quote]
> On Thu, Oct 25, 2012 at 1:21 AM, Thomas Rachel
> <nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa...@spamschutz.glglgl.de>
> wrote:
>>> j = next(j for j in iter(partial(randrange, n), None) if j not in
>>> selected)
>> This generator never ends. If it meets a non-matching value, it just skips
>> it and goes on.
> next() only returns one value. After it is returned, the generator is
> discarded, whether it has ended or not. If there were no valid values
> for randrange to select, then it would descend into an infinite loop.
> But then, so would the dropwhile and the original while loop.
You are completely right. My solution was right as well, but didn't match the problem...
Yours does indeed return one random value which is guaranteed not to be in selected.
Mine returns random values until the value is not in selected. I just misread the intention behind the while loop...
It seems the topic of this thread has changed drastically from the original message.
1) "while EXPR as VAR" in no way says that EXPR must be a boolean value. In fact, a use case I've run into commonly in web development is popping from a redis set. E.g.
client = StrictRedis()
while True:
profile_id = client.spop("profile_ids")
if not profile_id:
break
print profile_id
In this case, profile_id is "None" when the loop breaks. It would be much more straightforward (and more Pythonic, IMO), to write:
client = StrictRedis()
while client.spop("profile_ids") as profile_id:
print profile_id
2) Although not originally intended, I kind of like the "if" statement change proposed later in this thread. It certainly makes sense, since both while and if are "conditional" statements that are commonly followed by an assignment (or vice versa).
3) I don't think the use case I brought up is solved nicely by wrapping a function / lambda in a generator and using a for loop. E.g.
def helper(f):
value = f()
if value:
yield value
for profile_id in helper(lambda: client.spop("profile_ids")):
print profile_id
This works too, I guess
def helper(f, *args, **kwargs):
value = f(*args, **kwargs)
if value:
yield value
for profile_id in helper(client.spop, "profile_ids"):
print profile_id
Either way, it adds too much mental overhead. Every developer on a project has to now insert x lines of code before a for loop or import a helper method from some module, and do this every time this pattern reappears. It's not something I would want to do in one of my projects, since it makes things harder to understand. So all in all, it's a net negative from just doing things the canonical way (with the while / assignment pattern).
Dan Loewenherz <dloewenh...@gmail.com> writes:
> In this case, profile_id is "None" when the loop breaks. It would be
> much more straightforward (and more Pythonic, IMO), to write:
> client = StrictRedis()
> while client.spop("profile_ids") as profile_id:
> print profile_id
That is pretty loose, in my opinion. If the loop is supposed to return
a string until breaking on None, the break test should explicitly check
for None rather than rely on an implicit bool conversion that will also
test as false on an empty string. Code that handles strings should do
the right thing with the empty string. What you posted relies on an
unstated assumption that the strings that come back are never empty.
> it's a net negative from just doing things the canonical way (with the
> while / assignment pattern).
Yeah, the while/assignment is a bit ugly but it doesn't come up often
enough to be a bad problem, imho.
On Fri, Oct 26, 2012 at 5:06 PM, Paul Rubin <no.em...@nospam.invalid> wrote:
> Dan Loewenherz <dloewenh...@gmail.com> writes:
>> In this case, profile_id is "None" when the loop breaks. It would be
>> much more straightforward (and more Pythonic, IMO), to write:
>> client = StrictRedis()
>> while client.spop("profile_ids") as profile_id:
>> print profile_id
> That is pretty loose, in my opinion. If the loop is supposed to return
> a string until breaking on None, the break test should explicitly check
> for None rather than rely on an implicit bool conversion that will also
> test as false on an empty string.
while (client.spop("profile_ids") as profile_id) is not None:
print profile_id
Why is everyone skirting around C-style assignment expressions as
though they're simultaneously anathema and the goal? :)
But seriously, this new syntax would probably enhance Python somewhat,
but you're going to end up with odd edge cases where it's just as
almost-there as current syntax is for what this will solve. Is it
worth doing half the job?
On Fri, 26 Oct 2012 17:23:12 +1100, Chris Angelico wrote:
> Why is everyone skirting around C-style assignment expressions as though
> they're simultaneously anathema and the goal? :)
On Thursday, October 25, 2012 11:06:01 PM UTC-7, Paul Rubin wrote:
> Dan Loewenherz <dloewenh...@gmail.com> writes:
> > In this case, profile_id is "None" when the loop breaks. It would be
> > much more straightforward (and more Pythonic, IMO), to write:
> > client = StrictRedis()
> > while client.spop("profile_ids") as profile_id:
> > print profile_id
> That is pretty loose, in my opinion. If the loop is supposed to return
> a string until breaking on None, the break test should explicitly check
> for None rather than rely on an implicit bool conversion that will also
> test as false on an empty string. Code that handles strings should do
> the right thing with the empty string. What you posted relies on an
> unstated assumption that the strings that come back are never empty.
I think this is a good point. However, I can't think of any situation where I'd want to work with an empty string (in the applications I've worked with, at least).
We also don't special case things like this just because x is an empty string. If this "while EXPR as VAR" thing were to move forward, we shouldn't treat the truth testing any differently than how we already do. IMO we should write our applications with the understanding that '' will return False and work with that.
Here's a workaround BTW. Just have that method return a tuple, and do the truth testing yourself if you feel it's necessary.
while client.spop("profile_ids") as truthy, profile_id:
if not truthy:
break
print profile_id
Here, client.spop returns a tuple, which will always returns true. We then extract the first element and run a truth test on it. The function we use is in charge of determining the truthiness.
On Fri, Oct 26, 2012 at 9:29 AM, Dan Loewenherz <dloewenh...@gmail.com> wrote:
> while client.spop("profile_ids") as truthy, profile_id:
> if not truthy:
> break
> print profile_id
> Here, client.spop returns a tuple, which will always returns true. We then extract the first element and run a truth test on it. The function we use is in charge of determining the truthiness.
I don't like the idea of testing the first element. There's a large
element of surprise in doing that, I think. I would expect the truth
test to be the same with or without the existence of the "as" clause
there. That is, you should be able to remove the "as" clause and have
exactly the same behavior, just without the assignments. So it would
need to test the entire tuple.
That brings up an interesting additional question in my mind, though.
Should the while loop syntax attempt to perform the assignment on the
very last test, when the expression is false? I think there is a good
argument for doing so, as it will allow additional inspection of the
false value, if necessary. In the above, though, if the return value
is false (an empty tuple or None) then the assignment would fail
during unpacking, raising an exception.
Dan Loewenherz <dloewenh...@gmail.com> writes:
> We also don't special case things like this just because x is an empty
> string. If this "while EXPR as VAR" thing were to move forward, we
> shouldn't treat the truth testing any differently than how we already
> do. IMO we should write our applications with the understanding that
> '' will return False and work with that.
We don't "already" treat the truth testing any particular way because we
don't have this construction in the language at the moment. However,
it's well-established in Python that converting a string to a bool
results in False iff the string is empty.
The empty string is a perfectly good string and code that deals with
strings should handle the empty string properly, unless it knows the
string won't be empty. Basic modularity principles say to avoid putting
such knowledge into more of the code than necessary.
The conclusion is to not automatically convert the parameter to a bool.
However, if the "as" can be part of an expression as in Chris Angelico's
post, Chris's suggestion
while (client.spop("profile_ids") as profile_id) is not None:
print profile_id
looks good to me.
> while client.spop("profile_ids") as truthy, profile_id:
> if not truthy:
> break
This is ugly on two levels. First of all, if the .spop() still returns
None at the end of the input, the tuple unpacking will fail. Second,
the separate test and break defeats the purpose of the "while ... as"
construction. Might as well use the current style of assignment and
test inside the loop.
On 26Oct2012 09:10, Paul Rubin <no.em...@nospam.invalid> wrote:
| However, if the "as" can be part of an expression as in Chris Angelico's
| post, Chris's suggestion
| | while (client.spop("profile_ids") as profile_id) is not None:
| print profile_id
| | looks good to me.
Now this pulls me from a -0 to a +0.5.
Instead of burdening the control constructs with further structure, make
"as" a binding operation for keeping intermediate results from expressions.
It will work anywhere an expression is allowed, and superficially
doesn't break stuff that exists if "as" has the lowest precedence.
Any doco would need to make it clear that no order of operation is
implied, so that this:
x = 1
y = (2 as x) + x
does not have a defined answer; might be 2, might be 3. Just like any
other function call with side effects.
Speaking for myself (of course!), I definitely prefer this to adding
"as" as a post expression struction on if/while/etc.
I'm not +1 because to my mind it still presents a way for
assignment/binding to not be glaringly obvious at the left hand side of
an expression.
It would probably mean folding the except/with "as" uses back into
expressions and out of the control-structural part of the grammar. I can't
see that that would actually break any existing code though - anyone else?
On Fri, Oct 26, 2012 at 4:03 PM, Cameron Simpson <c...@zip.com.au> wrote:
> It will work anywhere an expression is allowed, and superficially
> doesn't break stuff that exists if "as" has the lowest precedence.
Please, no. There is no need for it outside of while expressions, and
anywhere else it's just going to be bad practice. Even if it's
considered an expression, let's only allow it in while expressions.
> Any doco would need to make it clear that no order of operation is
> implied, so that this:
> x = 1
> y = (2 as x) + x
> does not have a defined answer; might be 2, might be 3. Just like any
> other function call with side effects.
Actually, the docs are clear that expressions are evaluated left to
right, so the expected result of the above would be 4.
> It would probably mean folding the except/with "as" uses back into
> expressions and out of the control-structural part of the grammar. I can't
> see that that would actually break any existing code though - anyone else?
Yes it would, because the meaning is a bit different in both of those
cases. For except, the result of the expression (an exception class
or tuple of classes) is not stored in the target; the exception
*instance* is. Similarly for with, the result of the expression is
not stored; the result of calling its __enter__ method is, which is
often but not always the same thing.
On Fri, Oct 26, 2012 at 1:12 AM, Dan Loewenherz <dloewenh...@gmail.com> wrote:
> It seems the topic of this thread has changed drastically from the original message.
> 1) "while EXPR as VAR" in no way says that EXPR must be a boolean value. In fact, a use case I've run into commonly in web development is popping from a redis set. E.g.
> client = StrictRedis()
> while True:
> profile_id = client.spop("profile_ids")
> if not profile_id:
> break
> print profile_id
> In this case, profile_id is "None" when the loop breaks. It would be much more straightforward (and more Pythonic, IMO), to write:
> client = StrictRedis()
> while client.spop("profile_ids") as profile_id:
> print profile_id
For loops are pythonic. You can do this in Python today:
client = StrictRedis()
for profile_id in iter(lambda: client.spop("profile_ids"), None):
pass
I would like a better iter(), rather than a better while loop. It is
irritating to pass in functions that take arguments, and it is
impossible to, say, pass in functions that should stop being iterated
over when they return _either_ a None or a, say, False.
On Fri, Oct 26, 2012 at 2:23 AM, Chris Angelico <ros...@gmail.com> wrote:
> while (client.spop("profile_ids") as profile_id) is not None:
> print profile_id
> Why is everyone skirting around C-style assignment expressions as
> though they're simultaneously anathema and the goal? :)
Why should these two statements behave differently? :(
with foo() as bar: bar.baz()
with (foo() as bar): bar.baz()
I don't understand why everyone is so attached to this "as" syntax.
It's confusing because it behaves subtly differently than how it works
in "with", and it puts the variable on the wrong side of the
assignment operator.
> On 26Oct2012 09:10, Paul Rubin <no.em...@nospam.invalid> wrote:
> | while (client.spop("profile_ids") as profile_id) is not None:
> Now this pulls me from a -0 to a +0.5.
> Any doco would need to make it clear that no order of operation is
> implied, so that this:
> x = 1
> y = (2 as x) + x
> does not have a defined answer; might be 2, might be 3. Just like any
> other function call with side effects.
I really don't like undefined (or underdefined) specs. If it was to
be PEP'd out, I'd want to address as many edge cases as possible.
Such as
y = (2 as x) + (3 as x) + (4 as x)
y = (2 as x) + 4 as x
y = booleanish and (2 as x) or (4 as x)
y = booleanish and 2 or 4 as x
y = (2 as x) if booleanish else (3 as x)
y = (2 as x) if booleanish else (3 as z)
regardless of how "$PEJORATIVE, that's a dumb thing to do!" it is.
I hate C for how underdefined a lot of corners are. ("amongst my
hatreds of C are such diverse elements as: underdefined corners, a
pitiful standard library, the ease of shooting yourself in the foot,
...")
> I'm not +1 because to my mind it still presents a way for
> assignment/binding to not be glaringly obvious at the left hand side of
> an expression.
I think this is why I like it in the "while" (and could be twisted
into accepting it for "if") because it also introduces an
implied/actual scope for which the variable is intended. In an
arbitrary evaluation/assignment, it's much easier to lose the
"definition" nature of it at the top of a block.
On Fri, Oct 26, 2012 at 6:03 PM, Cameron Simpson <c...@zip.com.au> wrote:
> Any doco would need to make it clear that no order of operation is
> implied, so that this:
> x = 1
> y = (2 as x) + x
> does not have a defined answer; might be 2, might be 3. Just like any
> other function call with side effects.
But function calls with side effects _do_ have a defined order of
evaluation. Left to right. And the answer should be 4.
On 26Oct2012 19:41, Devin Jeanpierre <jeanpierr...@gmail.com> wrote:
| On Fri, Oct 26, 2012 at 6:03 PM, Cameron Simpson <c...@zip.com.au> wrote:
| > Any doco would need to make it clear that no order of operation is
| > implied, so that this:
| >
| > x = 1
| > y = (2 as x) + x
| >
| > does not have a defined answer; might be 2, might be 3. Just like any
| > other function call with side effects.
| | But function calls with side effects _do_ have a defined order of
| evaluation. Left to right.
| And the answer should be 4.
| http://docs.python.org/reference/expressions.html#evaluation-order
No. Separate _expressions_ are evaluated left to right.
So this:
f(1), f(2)
calls "f(1)" first, then "f(2)". But this:
f(1) + f(2)
need not do so. Counter-documentation welcomed, but the doco you cite
does not define an order for the second example above.
That may just be a coincidence of implementation - there's no special
reason to change the evaluation order form the lexical order there, but
expression optimisers should have a free hand generally.
On Fri, 26 Oct 2012 16:48:05 -0600, Ian Kelly wrote:
> On Fri, Oct 26, 2012 at 4:03 PM, Cameron Simpson <c...@zip.com.au> wrote:
>> It will work anywhere an expression is allowed, and superficially
>> doesn't break stuff that exists if "as" has the lowest precedence.
> Please, no. There is no need for it outside of while expressions,
There's no need for it *inside* of while expressions. It doesn't add to the expressiveness of the language, or increase the power of the language, or help people write correct code. It saves one trivial line of code in some, but not all, while loops, at the cost of increasing the complexity of the language and parser.
-1
> and
> anywhere else it's just going to be bad practice. Even if it's
> considered an expression, let's only allow it in while expressions.
While loops are not so special. If it will be bad practice elsewhere, it will be bad practice in while expressions too.
On 26Oct2012 18:26, Tim Chase <s...@thechases.com> wrote:
| On 10/26/12 17:03, Cameron Simpson wrote:
| > On 26Oct2012 09:10, Paul Rubin <no.em...@nospam.invalid> wrote:
| > | while (client.spop("profile_ids") as profile_id) is not None:
| > | > Now this pulls me from a -0 to a +0.5.
| > | > Any doco would need to make it clear that no order of operation is
| > implied, so that this:
| > | > x = 1
| > y = (2 as x) + x
| > | > does not have a defined answer; might be 2, might be 3. Just like any
| > other function call with side effects.
| | I really don't like undefined (or underdefined) specs. If it was to
| be PEP'd out, I'd want to address as many edge cases as possible.
I would not. Big time. One of the reasons C is so successful as a
lower level language is that by defining only what is needed to be
defined to make it _possible_ to get predictable and useful behaviour,
maximum flexibility is left for implementation to adapt to particular
environments as freely as possible.
Pinning down all the corner cases is in general a bad idea, IMO. Every
corner case you pin down is an early decision for inflexibility that may
later prove to be illfounded.
Specify what _needs_ to be specified to achieve the required effect.
And stay the hell away from things that only constraint outcomes while
not providing the required effect.
To take an obvious counter example: your stance would encourage defining
the iteration order of dictionary keys. To no good purpose, merely to
present definition of _all_ operations instead of just the necessary
operations.
| Such as
| y = (2 as x) + (3 as x) + (4 as x)
| y = (2 as x) + 4 as x
I would want these to be ambiguous, myself.
| y = booleanish and (2 as x) or (4 as x)
| y = booleanish and 2 or 4 as x
| y = (2 as x) if booleanish else (3 as x)
| y = (2 as x) if booleanish else (3 as z)
None of these is ambiguous. The second one could be harder to read and
want some brackets purely for clarity, but it is perfectly well defined
in outcome already.
| regardless of how "$PEJORATIVE, that's a dumb thing to do!" it is.
No, sometimes dumb things should remain bad ideas. Defining them to all
just makes them no longer obviously bad ideas, merely subtly bad ideas.
And subtly bad ideas are worse!
| I hate C for how underdefined a lot of corners are. ("amongst my
| hatreds of C are such diverse elements as: underdefined corners, a
| pitiful standard library, the ease of shooting yourself in the foot,
| ...")
The C standard library was pretty good for when it came out. And the higher
up the available facilities tree you go the more choices there are about
how something should be done. C is still one of my favourite languages
in its domain, though I haven't used it much for several years.
If you're uncomfortable with C, try to stay away from it. (I try to stay
away from C++ and its bretheren for similar reasons.)
| > I'm not +1 because to my mind it still presents a way for
| > assignment/binding to not be glaringly obvious at the left hand side of
| > an expression.
| | I think this is why I like it in the "while" (and could be twisted
| into accepting it for "if") because it also introduces an
| implied/actual scope for which the variable is intended.
A conceptual scope, sure. Presumably not a real one in Python...
| In an
| arbitrary evaluation/assignment, it's much easier to lose the
| "definition" nature of it at the top of a block.