views:

166

answers:

6

For some reason this function confused me:

def protocol(port):
    return port == "443" and "https://" or "http://"

Can somebody explain the order of what's happening behind the scenes to make this work the way it does.

I understood it as this until I tried it:

Either A)

def protocol(port):
    if port == "443":
        if bool("https://"):
            return True
    elif bool("http://"):
        return True
    return False

Or B)

def protocol(port):
    if port == "443":
        return True + "https://"
    else:
        return True + "http://"

Is this some sort of special case in Python, or am I completely misunderstanding how statements work?

+6  A: 

and returns the right operand if the left is true. or returns the right operand if the left is false. Otherwise they both return the left operand. They are said to coalesce.

Ignacio Vazquez-Abrams
Ah, thanks Ignacio. I don't know how I've gotten so far in Python without understanding this. It's the first time I've ever seen it used exactly like this. +1
orokusaki
http://docs.python.org/tutorial/datastructures.html#more-on-conditions
wRAR
+20  A: 

It's an old-ish idiom; inserting parentheses to show priority,

(port == "443" and "https://") or "http://"

x and y returns y if x is truish, x if x is falsish; a or b, vice versa, returns a if it's truish, otherwise b.

So if port == "443" is true, this returns the RHS of the and, i.e., "https://". Otherwise, the and is false, so the or gets into play and returns `"http://", its RHS.

In modern Python, a better way to do translate this old-ish idiom is:

"https://" if port == "443" else "http://"
Alex Martelli
@Alex +1 Thanks for the in-depth explanation. They've apparently made it so that I can't accept an answer for 10 minutes :( but great answer.
orokusaki
@orokusaki, thanks, and I'm sure you'll be able to accept it later;-).
Alex Martelli
+1 for mentioning python's ternary operator, much easier to read/understand imho
tosh
I learned a long time ago to use parentheses to make the interpreter/compiler do what *I* want the code to do, rather than be at the mercy of precedence. It helps to document the code for me and any who are maintaining it after I've passed it on. I consider it part of coding defensively.
Greg
+2  A: 

This is an ugly hack that is not recommended. It works because of the short-circuiting behaviour of and and or and that they return the one of their arguments rather than a boolean value. Using this technique gives a risk of introducing hard-to-find bugs, so don't use it in new code.

Here's an example of how the and/or idiom can give an unexpected result:

>>> foo = 'foobar'
>>> bar = 'foobar'
>>> x = 0
>>> y = 1
>>> (foo == bar) and x or y   # Will this return the value of x if (foo == bar)?
1

Prefer instead the newer notation:

return "https://" if port == "443" else "http://"
Mark Byers
@Mark That's the version I'm familiar with, and I've never seen the version in my post until today. +1
orokusaki
+1  A: 

You may want to read up on the "and / or trick" of Python in this article The Peculiar Nature of And and Or in Python. It's a bit like the IIF() in VBA or VB, or ?: in C-style languages.

Abel
@Abel Thanks. I just read it, interesting article. I've used the `return a or b` syntax plenty but for some reason this snippet does't jive.
orokusaki
A: 

This construction works because it 'unfolds' to the following code:

a and b -->

if a:
  return b
else:
  return a

a or b -->

if a:
  return a
else:
  return b
Yaroslav
+3  A: 

C and X or Y is the long-running early attempt by Python users to proxy for C ? X : Y

For the most part it works, except if X is False -- this has led to many bugs in Python code, so in the Python FAQ, you'll find the more correct solution being (C and [X] or [Y])[0] because a list with a single element, regardless of its evaluated Boolean value, is always True! For example: [None] is True but None isn't. The OP's example above works because the string representing X is not empty.

However, all this changed in Python 2.5, when the ternary or conditional operator was added to the language, allowing you to use the cleaner X if C else Y as stated in other posts here. If you see code using the older format, it's because the user has been a long time Python programmer who hasn't adopted the new syntax yet, they cut-n-paste other old code, or their employer is still using 2.4.x (or earlier releases), etc.

wescpy