Dive into python and-or fail - python

I am stuck at this particular example from dive into python
Example 4.18. When the and−or Trick Fails
>>>>a = ""
>>>>b = "second"
>>>1 and a or b
>>>>'second'
Since a is an empty string, which Python considers false in a boolean context, 1 and '' evalutes to '', and
then '' or 'second' evalutes to 'second'. Oops! That's not what you wanted.
The and−or trick, bool and a or b, will not work like the C expression bool ? a : b when a is false in a
boolean context.
Why does it says it isn't what the user wants, I mean 1 and "" would evaluate to False, while "" or b will evaluate to "second", that's perfectly what should happen, I don't understand why is it wrong?am I missing something?

No, you're not missing something.
The expr if cond else expr inline conditional was introduced to Python 2.5 in PEP 308; before that, conditionals had to be of the full form
if cond:
expr
else:
expr
However, people noticed that expr and cond or expr sort-of worked as an inline conditional, as long as the first expressions happened to evaluate to boolean true. As most things in Python are true, this syntax generally tended to work -- and then, of course, sometimes fail. This was in fact the motivation for adding the "proper" inline conditional.

The "and-or trick" was used to emulate ternary conditional operator, condition ? if-true : if-false or similar. Since 1 is equivalent to True in boolean context, the expression is expected to yield a, not b. So, the lesson is, don't use and-or trick. Python has ternary conditional since 2.5:
true if condition else false

Dive into python is not a good tutorial and should not be followed
The x and y or z expression was used before python had a proper ternary expression (y if x else z). This section is saying why it can surprise you if you expect it to behave like C's x ? y : z.
There's a list of tutorials on the python wiki. that might give you a more up to date resource.

Why does it says it isn't what the user wants, I mean 1 and "" would
evaluate to False, while "" or b will evaluate to "second", that's
perfectly what should happen, I don't understand why is it wrong?am I
missing something?
I feel this part of the question is being ignored. condition and if-true or if-false is a trick used in versions >2.5, as explained in other answers, to mimic other languages' condition ? if-true : if-false or later versions' if-true if condition else if-false.
The problem with it is that if if-true is an object that evaluates to false ("", [], (), False or 0), then this would fail, as expected when you look at the evaluation, but which might creep in as a bug if if-true is dynamically set and can evaluate to false.
The naively expected result of condition and a or b would have been the contents of a ("", empty string), as the condition (1) evaluates to true, but as 1 and a evaluates to false so you get the contents of b.
The solution to this problem is to either use the Python 2.5+ syntax (if-true if condition else if-false) or make sure that if-true is never false. One way to accomplish the second is to pack if-true and if-false in a tuple or list, as a nonempty iterable is always true:
>>> a = ""
>>> b = "second"
>>> condition = 1
>>> condition and a or b
'second'
>>> condition and [a] or [b]
['']
>>> (condition and [a] or [b])[0]
''
It's a kind of ugly solution, but it's there if you need to target old versions of Python.

Related

Order of evaluation of logical AND (python)

In python, which is more efficient:
if a:
if b:
# do something
or
if a and b:
# do something
The latter would be more efficient if b isn't calculated when a is false. But I can't seem to pinpoint whether that's the case in Python docs. Maybe someone can point me to it?
Short-Circuiting
Python doesn't run Y in X and Y if X is false. You can try it out yourself:
if True and print("Hello1"):
pass
if False and print("Hello2"):
pass # "Hello2" won't be printed
The reason Python does this is it has something called short-circuiting which optimising logic expressions like this one. Python realises that if if X is false then there is no point in checking Y because the whole expression will be false anyway.
How to Avoid Short-Circuiting
You can bypass this in Python by using the bitwise versions of the logic operators:
and -> &
or -> |
For example:
if True & bool(print("Hello1")):
pass
if False & bool(print("Hello2")):
pass # "Hello2" will be printed this time
Note however that you need to wrap print in bool because bitwise only works on the same data types.
Performance
I would imagine performance wise the one with only one if statement would be faster as other wise python might have to go through 2 if statements if it finds the conditions are true.
This is called short-circuiting, and it is mentioned in the docs.
And the answer is, Python will not evaluate b if a is False.
It evaluates from left to right, simple experiment
def iftrue1():
print("iftrue1")
return True
def iftrue2():
print("iftrue2")
return True
if iftrue1() and iftrue2():
pass
This outputs
iftrue1
iftrue2

Struggling with the order of operations. Am I overthinking this?

I've been given the following, and I'm asked to give the type of value and the expression it returns:
>>> b = 10
>>> c = b > 9
>>> c
I know that in the first part, we're defining b to be 10, but in the second sentence, I'm interpreting this as: Define c to be equal to b>9. Now b>9 as a value, doesn't make sense, so c can't be equal to it, so I put that the answer was error and the type was Nonetype.
The correct answer is apparently True, but why? Why do we take the c=b part first, and then ask whether it's >9? Is there some sort of standard order in which you're supposed to apply these things?
PS: What do the three >>> symbols mean in programming? I'm doing an introductory CS course, so please forgive any misnomers.
Python's order precedence is well documented. b > 9 returns a boolean value that must be evaluated before it can be assigned with c =.
And >>> is part of the interpreter REPL. It doesn't have a specific meaning to all programming languages.
You could run your code in any Python interpreter to see what the output values are. I'm not sure what you mean by getting a Nonetype error as nothing is evaluated to None in those lines
I think you're getting confused between:
the assignment operator (=), which assigns the result of the expression on the right side of the operator to the variable on the left side of the operator and;
the equality operator (==), which tests the expressions on the right and left of the operator for equality and returns a boolean (true/false) value.
The first expression assigns the value 10 to the variable b. The second expression assigns the expression b > 9 (i.e. 10 > 9), which evaluates to true, to c. Therefore, I hope you can see how c ends up being true.
The other issue you might need clarification on is that the = operator is right associative, which means that the expression to the right of the operator will be evaluated first. i.e. in the second line, b > 9 is evaluated first before assigning the result (true) to c.
In answer to the second part of your question. Your code wouldn't actually compile as it stands in a regular C# compiler. I'm not sure what the >>> are. Are you using an online editor or something?
Valid C# code would be:
int b = 10;
bool c = b > 9;
Console.WriteLine(c); //Outputs true

A better way to write: a = b if a is None else a

There is a very common sentence that I use over and over in Python that feels "too long", and I am hoping there is a more Pythonic way to write it:
a = b if a is None else a
Is there a better way to write this?
The typical shortcut for this is
a = a or b
Though, this will only test if a evaluates to False, so 0, False, None, '', etc. will all cause a to be set to b
You could do...
if a is None:
a = b
It seems a bit cleaner to me and you don't have to assign a variable to itself.
The standard approach is to short circuit as:
a = a or b
However this is dangerous if its only None that you consider needs to be replaced. For example, if you want to retain values of a like False then this approach will not do exactly as you desire.
A shorter phrase than your original one, but a rather ugly one, is
a = [a, b][a is None]
It's not to be recommmended, and can cause some mind-bending to readers, but was a common approach before the introduction of the inline if ... else ... syntax
Try
a = a or b
Python performs short-circuit evaluations, so if a evaluates to True (any non-zero integer or float, non-empty lists, non-empty dicts, etc), the value of a is return and be is not evaluated. However, if a == None, it evaluates to False, so the interpreter must also evaluate b. If b evaluates to True, then it will be returned as the answer. There are a couple of cases to watch out for, as mentioned in another answer.
There sure is: short-circuiting.
a = a or b

Is 'or' used on the right-hand-side of an assignment pythonic?

Situation
(Note: The following situation is just exemplary. This question applys to anything that can evaluate to bool)
A default list should be used if the user does not provide a custom list:
default_list = ...
custom_list = ...
if custom_list:
list = custom_list
else:
list = default_list
You can shorten this to:
default_list = ...
custom_list = ...
list = custom_list if custom_list else default_list
Now, as per https://docs.python.org/2.7/reference/expressions.html#or ...
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.
..., or does not return a boolean, but rather the first value whose boolean conversion is not false. Therefore, the following would be valid code:
list = custom_list or default_list
This is similar to the C# Null Coalescing Operator, except it should be recoined in Python as False Coalescing Operator, which returns the first non-false argument.
Question
The last example seems to be easier to read, but is it considered pythonic?
Neither pep8 (the program) nor pylint do complain.
That is perfectly valid and you can use that. Even the documentation of or has an example for the same.
Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.
However, the or method has a limitation. If you want to purposefully allow a Non-Truthy value, then it is not possible with that.
Let us say you want to allow an empty list
my_list = [] or default_list
will always give default_list. For example,
print [] or [1, 2, 3]
# [1, 2, 3]
But with conditional expression we can handle it like this
custom_list if isinstance(custom_list, list) else default_list
Dusting off the older documents, quoting the BDFL's FAQ,
4.16. Q. Is there an equivalent of C's "?:" ternary operator?
A. Not directly. In many cases you can mimic a?b:c with a and b or
c, but there's a flaw: if b is zero (or empty, or None -- anything
that tests false) then c will be selected instead. In many cases you
can prove by looking at the code that this can't happen (e.g. because
b is a constant or has a type that can never be false), but in general
this can be a problem.
Steve Majewski (or was it Tim Peters?) suggested the following
solution: (a and [b] or [c])[0]. Because [b] is a singleton list it
is never false, so the wrong path is never taken; then applying [0] to
the whole thing gets the b or c that you really wanted. Ugly, but it
gets you there in the rare cases where it is really inconvenient to
rewrite your code using 'if'.
Yes, using or for it's short-circuiting properties was the norm, before the conditional expression construct was added to the language.
It was, and still is, perfectly pythonic to use:
foo = bar or baz
to fall back to a default if bar is false-y (evaluates to false in a boolean context). The fact that it short-circuits also lets you do things like:
foo = bar or expensive_calculation(baz) # only if bar is false-y
and not have expensive_calculation() execute if bar is truth-y (evaluates to true in a boolean context). Similarly, you can use and to ensure that preconditions are met:
foo = bar and bar(baz) # call `bar()` only if it is truth-y
You should use a conditional expression for:
foo = bar and spam or eggs
however. That's what the conditional expression aims to replace. The idea was that if bar is truth-y, then spam is picked, else eggs is picked. But that breaks down if spam is false-y! This was a common source of errors, while
foo = spam if bar else eggs
always picks spam if bar is truth-y.

Python - logical evaluation order in "if" statement

In Python we can do this:
if True or blah:
print("it's ok") # will be executed
if blah or True: # will raise a NameError
print("it's not ok")
class Blah:
pass
blah = Blah()
if blah or blah.notexist:
print("it's ok") # also will be executed
Can somebody point me to documentation on this feature?
Is it an implementation detail or feature of the language?
Is it good coding style to exploit this feature?
The or and and short circuit, see the Boolean operations documentation:
The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.
Note how, for and, y is only evaluated if x evaluates to a True value. Inversely, for or, y is only evaluated if x evaluated to a False value.
For the first expression True or blah, this means that blah is never evaluated, since the first part is already True.
Furthermore, your custom Blah class is considered True:
In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. (See the __nonzero__() special method for a way to change this.)
Since your class does not implement a __nonzero__() method (nor a __len__ method), it is considered True as far as boolean expressions are concerned.
In the expression blah or blah.notexist, blah is thus true, and blah.notexist is never evaluated.
This feature is used quite regularly and effectively by experienced developers, most often to specify defaults:
some_setting = user_supplied_value or 'default literal'
object_test = is_it_defined and is_it_defined.some_attribute
Do be wary of chaining these and use a conditional expression instead where applicable.
This is called short-circuiting and is a feature of the language:
http://docs.python.org/2/tutorial/datastructures.html#more-on-conditions
The Boolean operators and and or are so-called short-circuit operators: their arguments are evaluated from left to right, and evaluation stops as soon as the outcome is determined. For example, if A and C are true but B is false, A and B and C does not evaluate the expression C. When used as a general value and not as a Boolean, the return value of a short-circuit operator is the last evaluated argument.
It's the way the operators logical operators, specifically or in python work: short circuit evaluation.
To better explain it, consider the following:
if True or False:
print('True') # never reaches the evaluation of False, because it sees True first.
if False or True:
print('True') # print's True, but it reaches the evaluation of True after False has been evaluated.
For more information see the following:
The official python documentation on boolean operations
A stack overflow question regarding short circuitry in python
With the or operator, values are evaluated from left to right. After one value evaluates to True, the entire statement evaluates to True (so no more values are evaluated).
Official documentation
It's a feature of the language
There is nothing wrong with using its functionality

Categories