I was wondering if there is a reason why python evaluates the slicing/indexing after the definition of a list/tuple ?
(This question concerns Python Code Golf so I know it's not readable or "good practice", it's just about the accepted syntax and the fundamental behavior of the language.)
We can use index to mimic the ternary operator's behavior:
a if x > 9 else b
(b,a)[x>9] # way shorter
But this has an issue: the content of the tuple is evaluated before the condition in the index.
I created an example to illustrate the point: a function f that reduces the size of a string by one recursively till the string is empty, then returns 0
f = lambda s: f(s) if len(s:=s[:-1]) else 0
print(f("abc")) # works fine
f = lambda s: (0, f(s))[len(s:=s[:-1])==0]
print(f("abc")) # max recursion depth error
The recursion depth error occurs because the tuple definition is evaluated before the index. It means that what is in the slice/index doesn't matter, the function will be called again and again.
I don't really understand why python doesn't evaluate the slice/index before because even an obvious case like the following fails:
f = lambda: (0, f())[0]
f() # max recursion depth error
On top of that, it could benefit in terms of memory usage and runtime if we just evaluate the single element (or the slice) we want from the array and not every single element:
x = 2
print([long_computation(), other_long_computation(), 0][x])
Is there any reason not to evaluate the slice/index before the tuple definition ?
Humans read left-to-right, the parser reads left-to-right, the compiler just converts whatever is parsed into bytecode. It would make sense- and it's easier- to just parse it left-to-right rather than adding special cases for stuff that can be already done properly in the left-to-right fashion. Why do you need to do this anyways? You don't need to do it. You can already do it properly in the current parser and compiler. The complexity of this special case and how rare it's ever used, both factors are enough as reasons to not evaluate the slice before the tuple definition.
First, the code:
>>> False or 'hello'
'hello'
This surprising behavior lets you check if x is not None and check the value of x in one line:
>>> x = 10 if randint(0,2) == 1 else None
>>> (x or 0) > 0
# depend on x value...
Explanation: or functions like this:
if x is false, then y, else x
No language that I know lets you do this. So, why does Python?
It sounds like you're combining two issues into one.
First, there's the issue of short-circuiting. Marcin's answer addresses this issue perfectly, so I won't try to do any better.
Second, there's or and and returning the last-evaluated value, rather than converting it to bool. There are arguments to be made both ways, and you can find many languages on either side of the divide.
Returning the last-evaluated value allows the functionCall(x) or defaultValue shortcut, avoids a possibly wasteful conversion (why convert an int 2 into a bool 1 if the only thing you're going to do with it is check whether it's non-zero?), and is generally easier to explain. So, for various combinations of these reasons, languages like C, Lisp, Javascript, Lua, Perl, Ruby, and VB all do things this way, and so does Python.
Always returning a boolean value from an operator helps to catch some errors (especially in languages where the logical operators and the bitwise operators are easy to confuse), and it allows you to design a language where boolean checks are strictly-typed checks for true instead of just checks for nonzero, it makes the type of the operator easier to write out, and it avoids having to deal with conversion for cases where the two operands are different types (see the ?: operator in C-family languages). So, for various combinations of these reasons, languages like C++, Fortran, Smalltalk, and Haskell all do things this way.
In your question (if I understand it correctly), you're using this feature to be able to write something like:
if (x or 0) < 1:
When x could easily be None. This particular use case isn't very useful, both because the more-explicit x if x else 0 (in Python 2.5 and later) is just as easy to write and probably easier to understand (at least Guido thinks so), but also because None < 1 is the same as 0 < 1 anyway (at least in Python 2.x, so you've always got at least one of the two options)… But there are similar examples where it is useful. Compare these two:
return launchMissiles() or -1
return launchMissiles() if launchMissiles() else -1
The second one will waste a lot of missiles blowing up your enemies in Antarctica twice instead of once.
If you're curious why Python does it this way:
Back in the 1.x days, there was no bool type. You've got falsy values like None, 0, [], (), "", etc., and everything else is true, so who needs explicit False and True? Returning 1 from or would have been silly, because 1 is no more true than [1, 2, 3] or "dsfsdf". By the time bool was added (gradually over two 2.x versions, IIRC), the current logic was already solidly embedded in the language, and changing would have broken a lot of code.
So, why didn't they change it in 3.0? Many Python users, including BDFL Guido, would suggest that you shouldn't use or in this case (at the very least because it's a violation of "TOOWTDI"); you should instead store the result of the expression in a variable, e.g.:
missiles = launchMissiles()
return missiles if missiles else -1
And in fact, Guido has stated that he'd like to ban launchMissiles() or -1, and that's part of the reason he eventually accepted the ternary if-else expression that he'd rejected many times before. But many others disagree, and Guido is a benevolent DFL. Also, making or work the way you'd expect everywhere else, while refusing to do what you want (but Guido doesn't want you to want) here, would actually be pretty complicated.
So, Python will probably always be on the same side as C, Perl, and Lisp here, instead of the same side as Java, Smalltalk, and Haskell.
No language that i know lets you do this. So, why Python do?
Then you don't know many languages. I can't think of one language that I do know that does not exhibit this "shortcircuiting" behaviour.
It does it because it is useful to say:
a = b or K
such that a either becomes b, if b is not None (or otherwise falsy), and if not it gets the default value K.
Actually a number of languages do. See Wikipedia about Short-Circuit Evaluation
For the reason why short-circuit evaluation exists, wikipedia writes:
If both expressions used as conditions are simple boolean variables,
it can be actually faster to evaluate both conditions used in boolean
operation at once, as it always requires a single calculation cycle,
as opposed to one or two cycles used in short-circuit evaluation
(depending on the value of the first).
This behavior is not surprising, and it's quite straightforward if you consider Python has the following features regarding or, and and not logical operators:
Short-circuit evaluation: it only evaluates operands up to where it needs to.
Non-coercing result: the result is one of the operands, not coerced to bool.
And, additionally:
The Truth Value of an object is False only for None, False, 0, "", [], {}. Everything else has a truth value of True (this is a simplification; the correct definition is in the official docs)
Combine those features, and it leads to:
or : if the first operand evaluates as True, short-circuit there and return it. Or return the 2nd operand.
and: if the first operand evaluates as False, short-circuit there and return it. Or return the 2nd operand.
It's easier to understand if you generalize to a chain of operations:
>>> a or b or c or d
>>> a and b and c and d
Here is the "rule of thumb" I've memorized to help me easily predict the result:
or : returns the first "truthy" operand it finds, or the last one.
and: returns the first "falsy" operand it finds, or the last one.
As for your question, on why python behaves like that, well... I think because it has some very neat uses, and it's quite intuitive to understand. A common use is a series of fallback choices, the first "found" (ie, non-falsy) is used. Think about this silly example:
drink = getColdBeer() or pickNiceWine() or random.anySoda or "meh, water :/"
Or this real-world scenario:
username = cmdlineargs.username or configFile['username'] or DEFAULT_USERNAME
Which is much more concise and elegant than the alternative.
As many other answers have pointed out, Python is not alone and many other languages have the same behavior, for both short-circuit (I believe most current languanges are) and non-coercion.
"No language that i know lets you do this. So, why Python do?" You seem to assume that all languages should be the same. Wouldn't you expect innovation in programming languages to produce unique features that people value?
You've just pointed out why it's useful, so why wouldn't Python do it? Perhaps you should ask why other languages don't.
You can take advantage of the special features of the Python or operator out of Boolean contexts. The rule of thumb is still that the result of your Boolean expressions is the first true operand or the last in the line.
Notice that the logical operators (or included) are evaluated before the assignment operator =, so you can assign the result of a Boolean expression to a variable in the same way you do with a common expression:
>>> a = 1
>>> b = 2
>>> var1 = a or b
>>> var1
1
>>> a = None
>>> b = 2
>>> var2 = a or b
>>> var2
2
>>> a = []
>>> b = {}
>>> var3 = a or b
>>> var3
{}
Here, the or operator works as expected, returning the first true operand or the last operand if both are evaluated to false.
This seems like a simple question but I was unable to find a precedent. One answer here points it out without explaining why.
Using logical operators without two variables returns not a boolean but one of the variables - the first for OR and the second for AND.
'x' or 'y'
> 'x'
3 and 4
> 4
What's the reason for this behaviour?
The reason is that that is the most efficient way to shortcut evaluation of boolean expressions. With or Python returns the first truthy value that it encounters. It doesn't need to evaluate the rest to discover if the expression is true. Similarly, with and Python returns the first falsy value that it encounters. It doesn't need to evaluate the rest to discover if the expression is false.
If it bothers you that you get a non-boolean back, then wrap a call to bool() around your expression.
Why do we see Python assignments with or?
For example:
def my_function(arg_1=None, arg_2=0):
determination = arg_1 or arg_2 or 'no arguments given!'
print(determination)
return determination
When called with no arguments, the above function would print and return 'no arguments given!'
Why does Python do this, and how can one best make best use of this functionality?
What the "or" expression does on assignment:
We sometimes see examples of this in Python as a substitute for conditional expression with ternary assignments, (in fact, it helped inspire the language to add conditional statements).
x = a or b
If bool(a) returns False, then x is assigned the value of b
Identical Use-case of Conditional Expressions (i.e. Ternary Assignments)
Here's an example of such a conditional expression that accomplishes the same thing, but with perhaps a bit less mystery.
def my_function(arg_1=None, arg_2=0):
determination = arg_1 if arg_1 else arg_2 if arg_2 else 'no arguments given!'
print(determination)
return determination
Repeating this syntax too much is considered to be bad style, otherwise it's OK for one-liners. The downside is that it is a bit repetitive.
or Expressions
The base case, x or y returns x if bool(x) evaluates True, else it evaluates y, (see the docs for reference). Therefore, a series of or expressions has the effect of returning the first item that evaluates True, or the last item.
For example
'' or [] or 'apple' or () or set(['banana'])
returns 'apple', the first item that evaluates as True, and
'' or [] or ()
returns (), even though it evaluates as False.
Extended and usage
For contrast, x and y returns x if bool(x) evaluates as False, else it returns y.
It makes sense that and would work this way when you consider that all of the conditions in a conditional and series needs to evaluate as True for the control flow to proceed down that path, and that it makes no sense to continue evaluating those items when you come across one that is False.
The utility of using and for assignment is not immediately as apparent as using or, but it was historically used for ternary assignment. That is, before this more clear and straightforward construction was available:
a = x if condition else y
the equivalent formed with boolean operators was:
a = condition and x or z # don't do this!
which while the meaning is derivable based on a full understanding of Python and and or evaluation, is not nearly as readable as the ternary conditional, and is best avoided altogether.
Conclusion
Using Boolean expressions for assignment must be done carefully. Definitely never use and for assignment, which is confusing enough to be quite error-prone. Style mavens will find use of or for assignments less preferable (than the more verbose ternary, if condition else), but I have found that it is so common in the professional Python community that it could be considered idiomatic.
If you choose to use it, do so cautiously with the understanding that the final element, if reached, will always be returned regardless of its evaluation, so that final element should probably be a literal, so that you know you have a good default fallback for your variable.
I am new the forum so hope this question is not too elementary or that it has been asked before. While writing some code in python, I found that individual functions (here called function1, function2 and function3) returning true or false would have the same output written to screen, and therefore I would like to link them together like this;
if function1 or function2 or function3:
print "something"
I know that function3 will take much more time to run, so I would like to avoid it. As the condition is now written it seems to me that it would be great for me if Python first evaluates function1 to false, and then stops the evaluation of the other conditions because it knows that the if-condition is already broken. The other possibility is that the returning values of all three functions are found separately before the truth value of the combined expression is evaluated. Does anybody know the sequences of action in the if condition-evaluation?
Python already does this for you with a mechanism known as "short-circuit" evaluation.
When a Boolean expression is found to be False (for an and) or True (for an or) at any stage during the evaluation, the rest of the expression is not evaluated since the end-result is already determined at that point.
So the order you put things into your Boolean expression really matters.
This is really quite useful since you can do something like this:
if i != 0 and 2332.0 / i:
...
to avoid division by zero with a simple and expression (i.e., the division will never take place if i is zero).
Also, note: You do need () for your function calls to work.
Finally, this short-circuiting evaluation isn't unique to Python, lots of languages do this.
What you're talking about is called "short-circuiting", and python does indeed do it.
However, I think if you want this to work properly, you want to use the and operator, not or as False or True returns True whereas False and True returns False (without ever looking at the second value). For completeness, True or False returns True (without ever looking at False).
Also, In your example, you're not actually calling the functions ... to call a function in python, you need parentheses:
function1(args) #for example