Related
Apologies if this has been asked before, but I have searched in vain for an answer to my exact question. Basically, with Python 2.7, I have a program running a series of geoprocessing tools, depended on what is reqested via a series of True/False variables that the user adjusts in the script e.g.
x = True
if x:
run function
However, I have now discovered that x does not need to be literally "True" for the function to run. For example:
In: x = True
if x:
print True
Out: True
In: x = 123
if x:
print True
Out: True
In: x = 'False'
if x:
print True
Out: True
In: x = False
if x:
print True
Out:
So any value other than False appears to evaluate to True, which would not be the case for if x == True or if x is True. Seeing as PEP 8 strongly recommends only using the if x: variant, can anybody explain why this behaviour occurs? It seems that if x: is more a test for "if x is not False" or "if x exists". With that in mind, I believe I should be using if x is True: in this case, despite what PEP 8 has to say.
The following values in Python are false in the context of if and other logical contexts:
False
None
numeric values equal to 0, such as 0, 0.0, -0.0
empty strings: '' and u''
empty containers (such as lists, tuples and dictionaries)
anything that implements __bool__ (in Python3) to return False, or __nonzero__ (in Python2) to return False or 0.
anything that doesn't implement __bool__ (in Python3) or __nonzero__ (in Python2), but does implement __len__ to return a value equal to 0
An object is considered "false" if any of those applies, and "true" otherwise, regardless of whether it's actually equal to or identical with False or True
Now, if you've arranged that x is necessarily one of the objects True or False, then you can safely write if x. If you've arranged that the "trueness" of x indicates whether or not to perform the operation, regardless of type, then you can safely write if x. Where you can write that you should prefer to do so, since it's cleaner to read.
Normally, if it is allowed for x to take the value True then you're in one of those two cases, and so you would not write if x is True. The important thing is to correctly document the meaning of x, so that it reflects the test used in the code.
Python programmers are expected to know what's considered true, so if you just document, "runs the function if x is true", then that expresses what your original code does. Documenting it, "runs the function if x is True" would have a different meaning, and is less commonly used precisely because of the style rule in PEP8 that says to test for trueness rather than the specific value True.
However, if you wanted the code to behave differently in the case where x is an empty container from the case where it is None, then you would write something like if x is not None.
I'd like to add a short example where those 3 tests differ:
def test(x):
print(x, ":", bool(x), x == True, x is True)
test("something")
test(1)
test(True)
The output (pretty formatted):
# "something" : True False False
# 1 : True True False
# True : True True True
x = 'False'
x = 123
Are both True
Other truth values.
The document explains other values.
As far as the PEP8 reason, its far more semantic to read if this_file_is_green
Other falsey values include 0, '', []. You should just use the if x: version.
It goes without saying that you should write code that does what you need. But in most cases, you simply don't need to say == True or is True, because you don't need to distinguish True from other "truthy" values. So it's recommended to leave that out for simplicity.
The case where you definitely should use == True or is True is when you do need to distinguish True from other truthy values.
In your example, do you care about the difference between True and 123? That would tell you which way to code it.
One thing about coding == True or is True: it will raise a minor red flag when other developers read your code. They won't think it's wrong, they will just wonder why it's there and will want to know why it's important to treat True differently from other truthy values in this particular case.
In other words, if you don't need it, it's best not to use it.
The ability to say
if x:
...
is considered a feature. You can also specify when the test should be considered to pass or not for user defined classes (just define the method __nonzero__ in Python 2.x or __bool__ in Python 3).
For example for strings and containers like lists, dictionaries or sets the test if x ... means "if x is not empty".
Note that the rationale is not that this allows less code to write, but that resulting code is easier to read and to understand.
If you like instead to write if x is True ... have you considered to go farther down that path to if (x is True) is True ... or if ((x is True) is True) is True ... ? :-)
In Python 2.7, if a: and if a==True are not giving the same output for values different to 1. Here are some snippets of code to demonstrate the different behaviors:
with a=1
a=1
if a==True:
print (a,"True")
else:
print (a,"Not True")
output> (1,True)
a=1
if a:
print (a,"True")
else:
print (a,"Not True")
output> (1, True)
with a=2
a=2
if a:
print (a,"True")
else:
print (a,"Not True")
output> (2, True)
a=2
if a==True:
print (a,"True")
else:
print (a,"Not True")
output> (2, Not True)
if you use if x ,it means it has to evaluate x for its truth value.But when you use x ==True or x is True.It means checking whether type(x)==bool and whether x is True.
attention : x is True is no equal to bool(x)==True
when you use x is True , you are checking the id of x and True.
I'm trying to learn python and came across some code that is nice and short but doesn't totally make sense
the context was:
def fn(*args):
return len(args) and max(args)-min(args)
I get what it's doing, but why does python do this - ie return the value rather than True/False?
10 and 7-2
returns 5. Similarly, changing the and to or will result in a change in functionality. So
10 or 7 - 2
Would return 10.
Is this legit/reliable style, or are there any gotchas on this?
TL;DR
We start by summarising the two behaviour of the two logical operators and and or. These idioms will form the basis of our discussion below.
and
Return the first Falsy value if there are any, else return the last
value in the expression.
or
Return the first Truthy value if there are any, else return the last
value in the expression.
The behaviour is also summarised in the docs, especially in this table:
Operation
Result
x or y
if x is false, then y, else x
x and y
if x is false, then x, else y
not x
if x is false, then True, else False
The only operator returning a boolean value regardless of its operands is the not operator.
"Truthiness", and "Truthy" Evaluations
The statement
len(args) and max(args) - min(args)
Is a very pythonic concise (and arguably less readable) way of saying "if args is not empty, return the result of max(args) - min(args)", otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,
exp1 and exp2
Should (roughly) translate to:
r1 = exp1
if r1:
r1 = exp2
Or, equivalently,
r1 = exp2 if exp1 else exp1
Similarly,
exp1 or exp2
Should (roughly) translate to:
r1 = exp1
if not r1:
r1 = exp2
Or, equivalently,
r1 = exp1 if exp1 else exp2
Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.
But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. #Patrick Haugh summarises truthiness nicely in this post.
All values are considered "truthy" except for the following, which are
"falsy":
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[] - an empty list
{} - an empty dict
() - an empty tuple
'' - an empty str
b'' - an empty bytes
set() - an empty set
an empty range, like range(0)
objects for which
obj.__bool__() returns False
obj.__len__() returns 0
A "truthy" value will satisfy the check performed by if or while
statements. We use "truthy" and "falsy" to differentiate from the
bool values True and False.
How and Works
We build on OP's question as a segue into a discussion on how these operators in these instances.
Given a function with the definition
def foo(*args):
...
How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?
Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.
In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.
Note that an alternative way to write this function would be:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Or, more concisely,
def foo(*args):
return 0 if not args else max(args) - min(args)
If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.
How or Works
I explain the working of or in a similar fashion with a contrived example.
Given a function with the definition
def foo(*args):
...
How would you complete foo to return all numbers over 9000?
We use or to handle the corner case here. We define foo as:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
# [9004]
foo(1, 2, 3, 4)
# 'No number over 9000!'
foo performs a filtration on the list to retain all numbers over 9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here). If there exist no such numbers, then the result of the list comp is [] which is Falsy. So the second expression is now evaluated (a non-empty string) and is returned.
Using conditionals, we could re-write this function as,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
As before, this structure is more flexible in terms of error handling.
Quoting from Python Docs
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.
So, this is how Python was designed to evaluate the boolean expressions and the above documentation gives us an insight of why they did it so.
To get a boolean value just typecast it.
return bool(len(args) and max(args)-min(args))
Why?
Short-circuiting.
For example:
2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all
The same goes for or too, that is, it will return the expression which is Truthy as soon as it finds it, cause evaluating the rest of the expression is redundant.
Instead of returning hardcore True or False, Python returns Truthy or Falsey, which are anyway going to evaluate to True or False. You could use the expression as is, and it will still work.
To know what's Truthy and Falsey, check Patrick Haugh's answer
and and or perform boolean logic, but they return one of the actual values when they are comparing. When using and, values are evaluated in a boolean context from left to right. 0, '', [], (), {}, and None are false in a boolean context; everything else is true.
If all values are true in a boolean context, and returns the last value.
>>> 2 and 5
5
>>> 2 and 5 and 10
10
If any value is false in a boolean context and returns the first false value.
>>> '' and 5
''
>>> 2 and 0 and 5
0
So the code
return len(args) and max(args)-min(args)
returns the value of max(args)-min(args) when there is args else it returns len(args) which is 0.
Is this legit/reliable style, or are there any gotchas on this?
This is legit, it is a short circuit evaluation where the last value is returned.
You provide a good example. The function will return 0 if no arguments are passed, and the code doesn't have to check for a special case of no arguments passed.
Another way to use this, is to default None arguments to a mutable primitive, like an empty list:
def fn(alist=None):
alist = alist or []
....
If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall
Gotchas
Yes, there are a few gotchas.
fn() == fn(3) == fn(4, 4)
First, if fn returns 0, you cannot know if it was called without any parameter, with one parameter or with multiple, equal parameters :
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
What does fn mean?
Then, Python is a dynamic language. It's not specified anywhere what fn does, what its input should be and what its output should look like. Therefore, it's really important to name the function correctly. Similarly, arguments don't have to be called args. delta(*numbers) or calculate_range(*numbers) might describe better what the function is supposed to do.
Argument errors
Finally, the logical and operator is supposed to prevent the function to fail if called without any argument. It still fails if some argument isn't a number, though:
>>> fn('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Possible alternative
Here's a way to write the function according to the "Easier to ask for forgiveness than permission." principle:
def delta(*numbers):
try:
return max(numbers) - min(numbers)
except TypeError:
raise ValueError("delta should only be called with numerical arguments") from None
except ValueError:
raise ValueError("delta should be called with at least one numerical argument") from None
As an example:
>>> delta()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5
If you really don't want to raise an exception when delta is called without any argument, you could return some value which cannot be possible otherwise (e.g. -1 or None):
>>> def delta(*numbers):
... try:
... return max(numbers) - min(numbers)
... except TypeError:
... raise ValueError("delta should only be called with numerical arguments") from None
... except ValueError:
... return -1 # or None
...
>>>
>>> delta()
-1
Is this legit/reliable style, or are there any gotchas on this?
I would like to add to this question that it not only legit and reliable but it also ultra practical. Here is a simple example:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
Therefore you can really use it at your advantage. In order to be conscise this is how I see it:
Or operator
Python's or operator returns the first Truth-y value, or the last value, and stops
And operator
Python's and operator returns the first False-y value, or the last value, and stops
Behind the scenes
In python, all numbers are interpreted as True except for 0. Therefore, saying:
0 and 10
is the same as:
False and True
Which is clearly False. It is therefore logical that it returns 0
Yes. This is the correct behaviour of and comparison.
At least in Python, A and B returns B if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc). A is returned IFF A is essentially False or None or Empty or Null.
On the other hand, A or B returns A if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc), otherwise it returns B.
It is easy to not notice (or to overlook) this behaviour because, in Python, any non-null non-empty object evaluates to True is treated like a boolean.
For example, all the following will print "True"
if [102]:
print "True"
else:
print "False"
if "anything that is not empty or None":
print "True"
else:
print "False"
if {1, 2, 3}:
print "True"
else:
print "False"
On the other hand, all the following will print "False"
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
to understand in simple way,
AND : if first_val is False return first_val else second_value
eg:
1 and 2 # here it will return 2 because 1 is not False
but,
0 and 2 # will return 0 because first value is 0 i.e False
and => if anyone false, it will be false. if both are true then only it will become true
OR : if first_val is False return second_val else first_value
reason is, if first is false it check whether 2 is true or not.
eg:
1 or 2 # here it will return 1 because 1 is not False
but,
0 or 2 # will return 2 because first value is 0 i.e False
or => if anyone false, it will be true. so if first value is false no matter what 2 value suppose to be.
so it returns second value what ever it can be.
if anyone is true then it will become true. if both are false then it will become false.
I'm trying to learn python and came across some code that is nice and short but doesn't totally make sense
the context was:
def fn(*args):
return len(args) and max(args)-min(args)
I get what it's doing, but why does python do this - ie return the value rather than True/False?
10 and 7-2
returns 5. Similarly, changing the and to or will result in a change in functionality. So
10 or 7 - 2
Would return 10.
Is this legit/reliable style, or are there any gotchas on this?
TL;DR
We start by summarising the two behaviour of the two logical operators and and or. These idioms will form the basis of our discussion below.
and
Return the first Falsy value if there are any, else return the last
value in the expression.
or
Return the first Truthy value if there are any, else return the last
value in the expression.
The behaviour is also summarised in the docs, especially in this table:
Operation
Result
x or y
if x is false, then y, else x
x and y
if x is false, then x, else y
not x
if x is false, then True, else False
The only operator returning a boolean value regardless of its operands is the not operator.
"Truthiness", and "Truthy" Evaluations
The statement
len(args) and max(args) - min(args)
Is a very pythonic concise (and arguably less readable) way of saying "if args is not empty, return the result of max(args) - min(args)", otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,
exp1 and exp2
Should (roughly) translate to:
r1 = exp1
if r1:
r1 = exp2
Or, equivalently,
r1 = exp2 if exp1 else exp1
Similarly,
exp1 or exp2
Should (roughly) translate to:
r1 = exp1
if not r1:
r1 = exp2
Or, equivalently,
r1 = exp1 if exp1 else exp2
Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.
But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. #Patrick Haugh summarises truthiness nicely in this post.
All values are considered "truthy" except for the following, which are
"falsy":
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[] - an empty list
{} - an empty dict
() - an empty tuple
'' - an empty str
b'' - an empty bytes
set() - an empty set
an empty range, like range(0)
objects for which
obj.__bool__() returns False
obj.__len__() returns 0
A "truthy" value will satisfy the check performed by if or while
statements. We use "truthy" and "falsy" to differentiate from the
bool values True and False.
How and Works
We build on OP's question as a segue into a discussion on how these operators in these instances.
Given a function with the definition
def foo(*args):
...
How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?
Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.
In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.
Note that an alternative way to write this function would be:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Or, more concisely,
def foo(*args):
return 0 if not args else max(args) - min(args)
If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.
How or Works
I explain the working of or in a similar fashion with a contrived example.
Given a function with the definition
def foo(*args):
...
How would you complete foo to return all numbers over 9000?
We use or to handle the corner case here. We define foo as:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
# [9004]
foo(1, 2, 3, 4)
# 'No number over 9000!'
foo performs a filtration on the list to retain all numbers over 9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here). If there exist no such numbers, then the result of the list comp is [] which is Falsy. So the second expression is now evaluated (a non-empty string) and is returned.
Using conditionals, we could re-write this function as,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
As before, this structure is more flexible in terms of error handling.
Quoting from Python Docs
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.
So, this is how Python was designed to evaluate the boolean expressions and the above documentation gives us an insight of why they did it so.
To get a boolean value just typecast it.
return bool(len(args) and max(args)-min(args))
Why?
Short-circuiting.
For example:
2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all
The same goes for or too, that is, it will return the expression which is Truthy as soon as it finds it, cause evaluating the rest of the expression is redundant.
Instead of returning hardcore True or False, Python returns Truthy or Falsey, which are anyway going to evaluate to True or False. You could use the expression as is, and it will still work.
To know what's Truthy and Falsey, check Patrick Haugh's answer
and and or perform boolean logic, but they return one of the actual values when they are comparing. When using and, values are evaluated in a boolean context from left to right. 0, '', [], (), {}, and None are false in a boolean context; everything else is true.
If all values are true in a boolean context, and returns the last value.
>>> 2 and 5
5
>>> 2 and 5 and 10
10
If any value is false in a boolean context and returns the first false value.
>>> '' and 5
''
>>> 2 and 0 and 5
0
So the code
return len(args) and max(args)-min(args)
returns the value of max(args)-min(args) when there is args else it returns len(args) which is 0.
Is this legit/reliable style, or are there any gotchas on this?
This is legit, it is a short circuit evaluation where the last value is returned.
You provide a good example. The function will return 0 if no arguments are passed, and the code doesn't have to check for a special case of no arguments passed.
Another way to use this, is to default None arguments to a mutable primitive, like an empty list:
def fn(alist=None):
alist = alist or []
....
If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall
Gotchas
Yes, there are a few gotchas.
fn() == fn(3) == fn(4, 4)
First, if fn returns 0, you cannot know if it was called without any parameter, with one parameter or with multiple, equal parameters :
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
What does fn mean?
Then, Python is a dynamic language. It's not specified anywhere what fn does, what its input should be and what its output should look like. Therefore, it's really important to name the function correctly. Similarly, arguments don't have to be called args. delta(*numbers) or calculate_range(*numbers) might describe better what the function is supposed to do.
Argument errors
Finally, the logical and operator is supposed to prevent the function to fail if called without any argument. It still fails if some argument isn't a number, though:
>>> fn('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Possible alternative
Here's a way to write the function according to the "Easier to ask for forgiveness than permission." principle:
def delta(*numbers):
try:
return max(numbers) - min(numbers)
except TypeError:
raise ValueError("delta should only be called with numerical arguments") from None
except ValueError:
raise ValueError("delta should be called with at least one numerical argument") from None
As an example:
>>> delta()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5
If you really don't want to raise an exception when delta is called without any argument, you could return some value which cannot be possible otherwise (e.g. -1 or None):
>>> def delta(*numbers):
... try:
... return max(numbers) - min(numbers)
... except TypeError:
... raise ValueError("delta should only be called with numerical arguments") from None
... except ValueError:
... return -1 # or None
...
>>>
>>> delta()
-1
Is this legit/reliable style, or are there any gotchas on this?
I would like to add to this question that it not only legit and reliable but it also ultra practical. Here is a simple example:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
Therefore you can really use it at your advantage. In order to be conscise this is how I see it:
Or operator
Python's or operator returns the first Truth-y value, or the last value, and stops
And operator
Python's and operator returns the first False-y value, or the last value, and stops
Behind the scenes
In python, all numbers are interpreted as True except for 0. Therefore, saying:
0 and 10
is the same as:
False and True
Which is clearly False. It is therefore logical that it returns 0
Yes. This is the correct behaviour of and comparison.
At least in Python, A and B returns B if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc). A is returned IFF A is essentially False or None or Empty or Null.
On the other hand, A or B returns A if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc), otherwise it returns B.
It is easy to not notice (or to overlook) this behaviour because, in Python, any non-null non-empty object evaluates to True is treated like a boolean.
For example, all the following will print "True"
if [102]:
print "True"
else:
print "False"
if "anything that is not empty or None":
print "True"
else:
print "False"
if {1, 2, 3}:
print "True"
else:
print "False"
On the other hand, all the following will print "False"
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
to understand in simple way,
AND : if first_val is False return first_val else second_value
eg:
1 and 2 # here it will return 2 because 1 is not False
but,
0 and 2 # will return 0 because first value is 0 i.e False
and => if anyone false, it will be false. if both are true then only it will become true
OR : if first_val is False return second_val else first_value
reason is, if first is false it check whether 2 is true or not.
eg:
1 or 2 # here it will return 1 because 1 is not False
but,
0 or 2 # will return 2 because first value is 0 i.e False
or => if anyone false, it will be true. so if first value is false no matter what 2 value suppose to be.
so it returns second value what ever it can be.
if anyone is true then it will become true. if both are false then it will become false.
I'm trying to learn python and came across some code that is nice and short but doesn't totally make sense
the context was:
def fn(*args):
return len(args) and max(args)-min(args)
I get what it's doing, but why does python do this - ie return the value rather than True/False?
10 and 7-2
returns 5. Similarly, changing the and to or will result in a change in functionality. So
10 or 7 - 2
Would return 10.
Is this legit/reliable style, or are there any gotchas on this?
TL;DR
We start by summarising the two behaviour of the two logical operators and and or. These idioms will form the basis of our discussion below.
and
Return the first Falsy value if there are any, else return the last
value in the expression.
or
Return the first Truthy value if there are any, else return the last
value in the expression.
The behaviour is also summarised in the docs, especially in this table:
Operation
Result
x or y
if x is false, then y, else x
x and y
if x is false, then x, else y
not x
if x is false, then True, else False
The only operator returning a boolean value regardless of its operands is the not operator.
"Truthiness", and "Truthy" Evaluations
The statement
len(args) and max(args) - min(args)
Is a very pythonic concise (and arguably less readable) way of saying "if args is not empty, return the result of max(args) - min(args)", otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,
exp1 and exp2
Should (roughly) translate to:
r1 = exp1
if r1:
r1 = exp2
Or, equivalently,
r1 = exp2 if exp1 else exp1
Similarly,
exp1 or exp2
Should (roughly) translate to:
r1 = exp1
if not r1:
r1 = exp2
Or, equivalently,
r1 = exp1 if exp1 else exp2
Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.
But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. #Patrick Haugh summarises truthiness nicely in this post.
All values are considered "truthy" except for the following, which are
"falsy":
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[] - an empty list
{} - an empty dict
() - an empty tuple
'' - an empty str
b'' - an empty bytes
set() - an empty set
an empty range, like range(0)
objects for which
obj.__bool__() returns False
obj.__len__() returns 0
A "truthy" value will satisfy the check performed by if or while
statements. We use "truthy" and "falsy" to differentiate from the
bool values True and False.
How and Works
We build on OP's question as a segue into a discussion on how these operators in these instances.
Given a function with the definition
def foo(*args):
...
How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?
Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.
In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.
Note that an alternative way to write this function would be:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Or, more concisely,
def foo(*args):
return 0 if not args else max(args) - min(args)
If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.
How or Works
I explain the working of or in a similar fashion with a contrived example.
Given a function with the definition
def foo(*args):
...
How would you complete foo to return all numbers over 9000?
We use or to handle the corner case here. We define foo as:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
# [9004]
foo(1, 2, 3, 4)
# 'No number over 9000!'
foo performs a filtration on the list to retain all numbers over 9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here). If there exist no such numbers, then the result of the list comp is [] which is Falsy. So the second expression is now evaluated (a non-empty string) and is returned.
Using conditionals, we could re-write this function as,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
As before, this structure is more flexible in terms of error handling.
Quoting from Python Docs
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.
So, this is how Python was designed to evaluate the boolean expressions and the above documentation gives us an insight of why they did it so.
To get a boolean value just typecast it.
return bool(len(args) and max(args)-min(args))
Why?
Short-circuiting.
For example:
2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all
The same goes for or too, that is, it will return the expression which is Truthy as soon as it finds it, cause evaluating the rest of the expression is redundant.
Instead of returning hardcore True or False, Python returns Truthy or Falsey, which are anyway going to evaluate to True or False. You could use the expression as is, and it will still work.
To know what's Truthy and Falsey, check Patrick Haugh's answer
and and or perform boolean logic, but they return one of the actual values when they are comparing. When using and, values are evaluated in a boolean context from left to right. 0, '', [], (), {}, and None are false in a boolean context; everything else is true.
If all values are true in a boolean context, and returns the last value.
>>> 2 and 5
5
>>> 2 and 5 and 10
10
If any value is false in a boolean context and returns the first false value.
>>> '' and 5
''
>>> 2 and 0 and 5
0
So the code
return len(args) and max(args)-min(args)
returns the value of max(args)-min(args) when there is args else it returns len(args) which is 0.
Is this legit/reliable style, or are there any gotchas on this?
This is legit, it is a short circuit evaluation where the last value is returned.
You provide a good example. The function will return 0 if no arguments are passed, and the code doesn't have to check for a special case of no arguments passed.
Another way to use this, is to default None arguments to a mutable primitive, like an empty list:
def fn(alist=None):
alist = alist or []
....
If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall
Gotchas
Yes, there are a few gotchas.
fn() == fn(3) == fn(4, 4)
First, if fn returns 0, you cannot know if it was called without any parameter, with one parameter or with multiple, equal parameters :
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
What does fn mean?
Then, Python is a dynamic language. It's not specified anywhere what fn does, what its input should be and what its output should look like. Therefore, it's really important to name the function correctly. Similarly, arguments don't have to be called args. delta(*numbers) or calculate_range(*numbers) might describe better what the function is supposed to do.
Argument errors
Finally, the logical and operator is supposed to prevent the function to fail if called without any argument. It still fails if some argument isn't a number, though:
>>> fn('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Possible alternative
Here's a way to write the function according to the "Easier to ask for forgiveness than permission." principle:
def delta(*numbers):
try:
return max(numbers) - min(numbers)
except TypeError:
raise ValueError("delta should only be called with numerical arguments") from None
except ValueError:
raise ValueError("delta should be called with at least one numerical argument") from None
As an example:
>>> delta()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5
If you really don't want to raise an exception when delta is called without any argument, you could return some value which cannot be possible otherwise (e.g. -1 or None):
>>> def delta(*numbers):
... try:
... return max(numbers) - min(numbers)
... except TypeError:
... raise ValueError("delta should only be called with numerical arguments") from None
... except ValueError:
... return -1 # or None
...
>>>
>>> delta()
-1
Is this legit/reliable style, or are there any gotchas on this?
I would like to add to this question that it not only legit and reliable but it also ultra practical. Here is a simple example:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
Therefore you can really use it at your advantage. In order to be conscise this is how I see it:
Or operator
Python's or operator returns the first Truth-y value, or the last value, and stops
And operator
Python's and operator returns the first False-y value, or the last value, and stops
Behind the scenes
In python, all numbers are interpreted as True except for 0. Therefore, saying:
0 and 10
is the same as:
False and True
Which is clearly False. It is therefore logical that it returns 0
Yes. This is the correct behaviour of and comparison.
At least in Python, A and B returns B if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc). A is returned IFF A is essentially False or None or Empty or Null.
On the other hand, A or B returns A if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc), otherwise it returns B.
It is easy to not notice (or to overlook) this behaviour because, in Python, any non-null non-empty object evaluates to True is treated like a boolean.
For example, all the following will print "True"
if [102]:
print "True"
else:
print "False"
if "anything that is not empty or None":
print "True"
else:
print "False"
if {1, 2, 3}:
print "True"
else:
print "False"
On the other hand, all the following will print "False"
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
to understand in simple way,
AND : if first_val is False return first_val else second_value
eg:
1 and 2 # here it will return 2 because 1 is not False
but,
0 and 2 # will return 0 because first value is 0 i.e False
and => if anyone false, it will be false. if both are true then only it will become true
OR : if first_val is False return second_val else first_value
reason is, if first is false it check whether 2 is true or not.
eg:
1 or 2 # here it will return 1 because 1 is not False
but,
0 or 2 # will return 2 because first value is 0 i.e False
or => if anyone false, it will be true. so if first value is false no matter what 2 value suppose to be.
so it returns second value what ever it can be.
if anyone is true then it will become true. if both are false then it will become false.
I'm going through the LPTHW and I came across something I cannot understand. When will it ever be the case that you want your boolean and or or to return something other than the boolean? The LPTHW text states that all languages like python have this behavior. Would he mean interpreted vs. compiled languages or duck typed vs static typed languages?
I ran the following code:
>>> False and 1
False
>>> True and 1
1
>>> 1 and False
False
>>> 1 and True
True
>>> True and 121
121
>>> False or 1
1
>>> False or 112
112
>>> False or "Khadijah"
'Khadijah'
>>> True and 'Khadijah'
'Khadijah'
>>> False or 'b'
'b'
>>> b = (1, 2, "K")
>>> b
(1, 2, 'K')
>>> False or b
(1, 2, 'K')
>>>
Please help me understand whats going on here.
According to the documentation: http://docs.python.org/2/library/stdtypes.html
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)
According to LPTHW: http://learnpythonthehardway.org/book/ex28.html
Why does "test" and "test" return "test" or 1 and 1 return 1 instead of True?
Python and many languages like it return one of the operands to their boolean expressions rather than just True or False. This means that if you did False and 1 you get the first operand (False) but if you do True and 1 your get the second (1). Play with this a bit.
I think you're somehow confused about what the docs says. Take a look at these two docs sections: Truth Value Testing and Boolean Operators. To quote the last paragraph on the fist section:
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)
As you can see, you're right about operations and built-in functions but see the Important exception part, it is well stated that the Boolean operators will return one of their operands.
Now, what they can return depends hardly on the operator's short circuit logic. For or operator, it will return the first truthy value in the expression, since when it finds one, the whole expression is true. In case of every operand being falsey, or will return the last operand, meaning that it iterated over every one of them not being able to find a truthy one.
For and operator, if the expression is true, it will return the last operand, if the expression is false, it will return the first falsey operand. You can read more about Short Circuit Evaluation at the Wikipedia Page.
You have a lot of examples in your question, let's analyze some of them:
>>> False and 1 # return false (short circuited at first falsey value)
False
>>> True and 1 # return 1 (never short circuited and return the last truthy value)
1
>>> 1 and False # return false (short circuited at first falsey value, in this case the last operand)
False
>>> 1 and True # return True (never short circuited and return the last truthy value)
True
>>> True and 121 # return 121 (never short circuited and return the last truthy value)
121
>>> False or 1 # return 1 (since the first operand was falsey, or kept looking for a first truthy value which happened to be the last operator)
1
>>> False or 112 # return 112 for same reason as above
112
>>> False or "Khadijah" # return "Khadijah" for same reason as above
'Khadijah'
>>> True and 'Khadijah' # return "Khadijah" because same reason as second example
'Khadijah'
I think this should make a point. To help you further understand why this is useful, consider the following example:
You have a function that randomly generate names
import random
def generate_name():
return random.choice(['John', 'Carl', 'Tiffany'])
and you have a variable that you don't know if it has assigned a name yet so instead of doing:
if var is None:
var = generate_name()
You can do oneliner:
var = var or generate_name()
Since None is a falsey value, or will continue its search and evaluate second operand, this is, call the function ultimately returning the generated name. This is a very silly example, I have seen better usages (although not in Python) of this kind of style. I couldn't come out with a better example right now. You can also take a look at this questions, there are very useful answers on the topic: Does Python support short-circuiting?
Last but not least, this has nothing to do with static typed, duck typed, dynamic, interpreted, compiled, whatever language. It's just a language feature, one that might come handy and that is very common since almost every programming language I can think of provide this feature.
Hope this helps!
One would want and and or to evaluate to an operand (as opposed to always evaluating to True or False) in order to support idioms like the following:
def foo(self):
# currentfoo might be None, in which case return the default
return self.currentfoo or self.defaultfoo()
def foobar(self):
# foo() might return None, in which case return None
foo = self.foo()
return foo and foo.bar()
You can of course question the value of such idioms, especially if you aren't used to them. It's always possible to write equivalent code with an explicit if.
As a point against them, they leave some doubt whether the full range of falsey values is possible and intentionally accounted for, or just the one mentioned in the comment (with other falsey values not permitted). But then, this is true in general of code that uses the true-ness of a value that might be something other than True or False. It occasionally but rarely leads to misunderstandings.
This has to do with the way short circuit effect is implemented in Python.
With and (remember True and X = X), the result of the right expression is pushed into the stack, if it's false, it gets poped immediately, else the second expression is poped:
>>> import dis
>>>
>>> dis.dis(lambda : True and 0)
1 0 LOAD_CONST 2 (True)
3 JUMP_IF_FALSE_OR_POP 9
6 LOAD_CONST 1 (0)
>> 9 RETURN_VALUE
>>>
>>> True and 0
0
>>> 0 and True
0
>>>
similar to:
def exec_and(obj1, obj2):
if bool(obj1) != False:
return obj2
return obj1
With or, if the first expression is true, it gets popped immediately. If not, the second expression is poped, now the result really depends on the second.
>>> dis.dis(lambda : 0 or False)
1 0 LOAD_CONST 1 (0)
3 JUMP_IF_TRUE_OR_POP 9
6 LOAD_CONST 2 (False)
>> 9 RETURN_VALUE
>>>
>>> True or 0
True
>>> 1 or False
1
>>> False or 1
1
>>>
similar to:
def exec_or(obj1, obj2):
if bool(obj1) != True:
return obj2
return obj1
Consider the following use case:
element = dict.has_key('foo') and dict['foo']
Will set element to dict['foo'] if it exists, otherwise False. This is useful when writing a function to return a value or False on failure.
A further use case with or
print element or 'Not found!'
Putting these two lines together would print out dict['foo'] if it exists, otherwise it will print 'Not found!' (I use str() otherwise the or fails when element is 0 (or False) because that s considered falsey and since we are only printing it doesn't matter)
This can be simplified to
print dict.has_key('foo') and str(dict['foo']) or 'Not found!'
And is functionally equivalent to:
if dict.has_key('foo'):
print dict['foo']
else:
print 'Not found!'