Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #63006 > unrolled thread

Re: Ifs and assignments

Started byJohn Allsup <pydev@allsup.co>
First post2014-01-02 21:44 +0000
Last post2014-01-03 16:35 +1100
Articles 6 — 4 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Ifs and assignments John Allsup <pydev@allsup.co> - 2014-01-02 21:44 +0000
    Re: Ifs and assignments Roy Smith <roy@panix.com> - 2014-01-02 18:36 -0500
      Re: Ifs and assignments Chris Angelico <rosuav@gmail.com> - 2014-01-03 11:07 +1100
      Re: Ifs and assignments Chris Angelico <rosuav@gmail.com> - 2014-01-03 11:06 +1100
      Re: Ifs and assignments Ethan Furman <ethan@stoneleaf.us> - 2014-01-02 20:16 -0800
      Re: Ifs and assignments Chris Angelico <rosuav@gmail.com> - 2014-01-03 16:35 +1100

#63006 — Re: Ifs and assignments

FromJohn Allsup <pydev@allsup.co>
Date2014-01-02 21:44 +0000
SubjectRe: Ifs and assignments
Message-ID<mailman.4811.1388704420.18130.python-list@python.org>
The point of my original post was that, whilst C's
   if( x = 2 ) { do something }
and
   if( x == 2 ) { do something }
are easy to confuse, and a source of bugs, having a construct like follows:

if x == 2:
	do something # what happens at present
if testFunc() as x:
	do something with x

using the 'as' syntax that appears with 'with' and 'except', would allow
for the advantages of C style assignments in conditionals but without 
the easy confusion, since here the syntax is significantly different 
between assignment and equality testing (rather than a character apart 
as happens with C).

This occurs further down in my original post (past the point where you 
inserted your reply).

Another post suggested a workaround by defining a 'pocket' class, for 
which I am grateful.

John


On 02/01/2014 19:27, Gary Herron wrote:
> On 01/02/2014 09:20 AM, John Allsup wrote:
>> Hi,
>>
>> This is my debut on this list.
>>
>> In many languages, such as C, one can use assignments in conditionals
>> and expressions.  The most common, and useful case turns up when you
>> have if/else if/else if/else constructs. Consider the following
>> non-working pseudoPython.
>>
>> import re
>> r1 = re.compile("hello (\d)")
>> r2 = re.compile("world([!?])")
>>
>> w = "hello world!"
>>
>> if m = r1.search(w):
>
> This kind of thing in C/C+ has always been the source of much confusion
> and potential errors, because the construct is so similar to an "=="
> test.  Python does not replicate this potential for confusion. Instead,
> we use two lines of code, an assignment and then the test.   If you find
> that extra line of code inconvenient than at least you can take comfort
> in the fact that it is clearer code.  If you are still not convinced,
> ... then sorry, that's just the way Python is.
>
> Gary Herron
>
>
>>     handleMatch1(m)
>> elif m = r2.search(w):
>>     handleMatch2(m)
>> else:
>>     print("No match")
>>
>> If the regular expressions are complex, running them multiple times
>> (once to test, another to capture groups) isn't ideal.  On the other
>> hand, at present, one has to either do:
>>
>> m = r1.search(w)
>> if m:
>>     handleMatch1(m)
>> else:
>>     m = r2.search(w)
>>     if m:
>>         handleMatch2(m)
>>     else:
>>         print("No match")
>>
>> if not running unnecessary matches, yet capturing groups in the event
>> of a successful match, is what is desired.
>>
>> If there are multiple tests, the indentation gets silly.  This arises
>> because having removed the ability to assign in an expression, there
>> is no way to save the result of a function call that is used in a
>> conditional at all.
>>
>> I am aware that this facility in C is a source of bugs, = being only a
>> typo away from the more common ==.  With exceptions and contexts, we
>> have:
>>
>> with open("file") as f:
>>     doSomethingWith(f)
>>
>> try:
>>     trySomething()
>> except SomethingRandomGoingWrong as e:
>>     lookAtException(e)
>>
>> What I am wondering is why not use a similar syntax with if, so that
>> one could do
>>
>> if r1.search(w) as m:
>>     g = m.groups()
>>     print(g[1])
>>
>> This would remove the risk of errors by typos since the syntax for
>> equality testing (if x == y:) is completely different from that for
>> assigning in a conditional (which would look like 'if y as x:'
>>
>> Related would be to have Nonetype work with contexts such that
>>
>> with None as x:
>>     doStuff(x)
>>
>> would do nothing.  This would allow things like:
>>
>> with maybeGetSomething as x:
>>     doStuff(x)
>>
>> to call doStuff(x) within a context of maybeGetSomething returns
>> something, or do nothing if nothing is returned.  (Adding an else-like
>> keyword to with, or possibly using else in that context, would allow
>> one to process a non-None object if returned, or else do something in
>> response to a None object being returned by the maybeGetSomething.)
>>
>> Just a thought.
>>
>> Or what is the current 'Pythonic' way to do something like:
>>
>> if x = func1():
>>     do1(x)
>> elif x = func2():
>>     do2(x)
>> elif x = func3():
>>     do3(x)
>> elif x = func4():
>>     do4(x)
>> else:
>>     do5()
>>
>> where each of func1,func2,func3,func4 have side effects so that func2
>> is tested if and only if func1 returns a false value, func1 must be
>> called only once, and what is returned from func1 must be available to
>> the code inside the if block?
>>
>>
>> John
>>
>

[toc] | [next] | [standalone]


#63007

FromRoy Smith <roy@panix.com>
Date2014-01-02 18:36 -0500
Message-ID<roy-8601CA.18361502012014@news.panix.com>
In reply to#63006
In article <mailman.4811.1388704420.18130.python-list@python.org>,
 John Allsup <pydev@allsup.co> wrote:

> if testFunc() as x:
> 	  do something with x

+1

The most common place I wish for an atomic "test and assign" is with 
regexes, as in your examples.  This would be so much nicer than what we 
have to do now:

    if re.match(string) as m:
        print m.group(0)

The analogy to

    except SomeError as ex:

is very clear.  They are both "Perform some action, capture the result, 
do some test on it, and then make that captured value available bound to 
a name".

[toc] | [prev] | [next] | [standalone]


#63011

FromChris Angelico <rosuav@gmail.com>
Date2014-01-03 11:07 +1100
Message-ID<mailman.4813.1388707688.18130.python-list@python.org>
In reply to#63007
On Fri, Jan 3, 2014 at 11:06 AM, Chris Angelico <rosuav@gmail.com> wrote:
> Pass any object through truth() and it'll either stay the same (if
> it's true) or become this object (if it's false). You can then carry
> on with other method calls, and they'll all happily return false.
>
> result = (
>     truth(re1.match(string)).group(0) or
>     truth(re2.match(string)).group(0) or
>     truth(re3.match(string)).group(0) or
>     default_value
> )

Oh, just thought of a possible snag. Since an empty string comes up as
false, this exact notation would fail if the re can match nothing. But
you probably know if it's possible for that to happen, so don't use
this simple short-hand. :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#63012

FromChris Angelico <rosuav@gmail.com>
Date2014-01-03 11:06 +1100
Message-ID<mailman.4814.1388708064.18130.python-list@python.org>
In reply to#63007
On Fri, Jan 3, 2014 at 10:36 AM, Roy Smith <roy@panix.com> wrote:
> The most common place I wish for an atomic "test and assign" is with
> regexes, as in your examples.  This would be so much nicer than what we
> have to do now:
>
>     if re.match(string) as m:
>         print m.group(0)

Here's a crazy idea. Suppose we have a "sticky falseness" that can
quietly propagate through an expression the way a NaN can... then we
could just float that right through the .group() call.

class truth:
    def __new__(cls, x):
        if x: return x
        return object.__new__(cls)
    def __bool__(self):
        return False
    def __getattr__(self, name):
        return self
    def __call__(self, *args, **kwargs):
        return self
    def __repr__(self):
        return repr(False)

Pass any object through truth() and it'll either stay the same (if
it's true) or become this object (if it's false). You can then carry
on with other method calls, and they'll all happily return false.

result = (
    truth(re1.match(string)).group(0) or
    truth(re2.match(string)).group(0) or
    truth(re3.match(string)).group(0) or
    default_value
)

(I'm not sure if I'm using __new__ correctly; I've never actually done
it in production code, and the info I found online was mainly Py2
examples. Should that be done with super(), or is that applicable only
once there's an actual instance with a real MRO?)

I've given the class a lower-case first letter as I'm basically using
this as a function. If this were ever to be considered for the
standard library I would expect to see that aspect of it heavily
bikeshedded, so I'm not too concerned about the details.

In a spirit of full disclosure, I must confess that the idea came from
Pike, where there's a variant member-lookup operator that will happily
return false from false rather than throwing an exception. :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#63034

FromEthan Furman <ethan@stoneleaf.us>
Date2014-01-02 20:16 -0800
Message-ID<mailman.4831.1388724082.18130.python-list@python.org>
In reply to#63007
On 01/02/2014 04:06 PM, Chris Angelico wrote:
>
> Here's a crazy idea. Suppose we have a "sticky falseness" that can
> quietly propagate through an expression the way a NaN can... then we
> could just float that right through the .group() call.
>
> class truth:
>      def __new__(cls, x):
>          if x: return x
>          return object.__new__(cls)
>      def __bool__(self):
>          return False
>      def __getattr__(self, name):
>          return self
>      def __call__(self, *args, **kwargs):
>          return self
>      def __repr__(self):
>          return repr(False)
>
> Pass any object through truth() and it'll either stay the same (if
> it's true) or become this object (if it's false). You can then carry
> on with other method calls, and they'll all happily return false.

An interesting idea.  You'd need to add (at least) __getitem__, and I'll 
probably call it `maybe`, myself.  ;)


> (I'm not sure if I'm using __new__ correctly; I've never actually done
> it in production code, and the info I found online was mainly Py2
> examples. Should that be done with super(), or is that applicable only
> once there's an actual instance with a real MRO?)

I haven't tested it, but your __new__ looks fine.  The only thing you 
lose by not calling super() is the inability for cooperative multiple 
inheritance, except as the ultimate base class.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#63038

FromChris Angelico <rosuav@gmail.com>
Date2014-01-03 16:35 +1100
Message-ID<mailman.4834.1388727330.18130.python-list@python.org>
In reply to#63007
On Fri, Jan 3, 2014 at 3:16 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
> On 01/02/2014 04:06 PM, Chris Angelico wrote:
>>
>>
>> Here's a crazy idea. Suppose we have a "sticky falseness" that can
>> quietly propagate through an expression the way a NaN can... then we
>> could just float that right through the .group() call.
>>
>> class truth:
>
> An interesting idea.  You'd need to add (at least) __getitem__, and I'll
> probably call it `maybe`, myself.  ;)

I was going for something like bool(). If you pass something through
bool(), you get either True or False; if you pass something through
this, you get either itself or something that acts like False.

>> (I'm not sure if I'm using __new__ correctly; I've never actually done
>> it in production code, and the info I found online was mainly Py2
>> examples. Should that be done with super(), or is that applicable only
>> once there's an actual instance with a real MRO?)
>
> I haven't tested it, but your __new__ looks fine.  The only thing you lose
> by not calling super() is the inability for cooperative multiple
> inheritance, except as the ultimate base class.

Is it possible to have multiple inheritance at this point, though? I
get a class argument, not an instance, because there isn't an
instance.

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web