Assigning string with boolean expression
Question:
I am trying to understand this code from someone else’s project. If you want the context it’s here: https://github.com/newsapps/beeswithmachineguns/blob/master/beeswithmachineguns/bees.py#L501
IS_PY2
is just a boolean variable, True
if the Python major version is 2.
I know that a non-empty string is True
, but for some reason I don’t understand openmode
is assigned either 'w'
or 'wt'
rather than True
or False
.
openmode = IS_PY2 and 'w' or 'wt'
openkwargs = IS_PY2 and {} or {'encoding': 'utf-8', 'newline': ''}
Could someone explain the result?
Answers:
The ternary boolean expression works as:
>>> 2 and 3 or 4
3
>>> 0 and 3 or 4
4
So, this expression:
openmode = IS_PY2 and 'w' or 'wt'
Become in Python 2:
openmode = True and 'w' or 'wt'
Which is equivalent to
openmode = 'w' or 'wt'
So, i gives w
.
Under Python 3, IS_PY2 is False, giving:
openmode = False and 'w' or 'wt'
Which is equivalent to
openmode = False or 'wt'
Giving wt
.
All of this is to specify explicitely that the openmode is for text files, not binary, which is indicated by w
in Python2 and wt
in Python3.
While the Python3 t
mode is the default one, this is not necessary to precise it.
See this answer about wt
mode.
Finally, i think that the following is much more readable:
openmode = 'w' if IS_PY2 else 'wt'
And this one, much more simple:
openmode = 'w'
The and
and or
operators don’t simply perform a boolean operation on their operands, giving a boolean result. The result they give is always one of their operands. These operators evaluate from left to right, with and
having a higher precedence than or
, and they short-circuit, meaning that they stop evaluating their operands as soon as possible.
In pure boolean logic, False and x
is False
, no matter what x
is, so there’s no need to examine x
. The Python expression False and x
will give a result of False
and it will not attempt to evaluate x
. Thus False and some_function()
will not call some_function()
.
Similarly, True and x
in pure boolean logic will have the same truth value as x
, i.e., if x
is True
then True and x
is True
, otherwise its False
.
But the Python and
operator can handle arbitrary operands.
In a and b
if a
is false-ish, then b
won’t be evaluated and the result will be a
. If a
is true-ish, then b
will be evaluated and become the result.
Here’s a short demo, using Python 2:
print False and 'boolean'
print 0 and 'integer'
print '' and 'string'
print [] and 'list'
print
print True and 'boolean'
print 7 and 'integer'
print 'a' and 'string'
print [42] and 'list'
print
print True and False
print True and 0
print True and ''
print True and []
print
output
False
0
[]
boolean
integer
string
list
False
0
[]
(Those blank lines between 0
and []
are where the empty string is getting printed).
Similar considerations apply to the or
operator.
In pure boolean logic, True or x
is True
, no matter what x
is so if the first part of an or
expression is True-ish we don’t need to evaluate the second part. And False or x
has the truth value of x
.
print False or 'boolean'
print 0 or 'integer'
print '' or 'string'
print [] or 'list'
print
print True or 'boolean'
print 7 or 'integer'
print 'a' or 'string'
print [42] or 'list'
print
print False or False
print False or 0
print False or ''
print False or []
print
output
boolean
integer
string
list
True
7
a
[42]
False
0
[]
As I said earlier, these operators are evaluated left to right, and we can chain them if we want. Here are the “classic” cases:
print True and 'yes' or 'no'
print False and 'yes' or 'no'
Those statements are equivalent to
print (True and 'yes') or 'no'
print (False and 'yes') or 'no'
output
yes
no
That construction was common in early versions of Python. These days, it’s far more common to see an if
expression:
print 'yes' if True else 'no'
print 'yes' if False else 'no'
Which is generally considered to be more readable than the ternary expression using and
and or
. Also, a and b or c
is not equivalent to b if a else c
if b
is false-ish.
However, it’s still important to understand how this ternary and ... or
thing works, especially if you need to read or maintain older code. And some old Pythonistas still prefer the and ... or
form, as it’s slightly shorter even if it is a little bewildering when you don’t understand how it works. 🙂
I am trying to understand this code from someone else’s project. If you want the context it’s here: https://github.com/newsapps/beeswithmachineguns/blob/master/beeswithmachineguns/bees.py#L501
IS_PY2
is just a boolean variable, True
if the Python major version is 2.
I know that a non-empty string is True
, but for some reason I don’t understand openmode
is assigned either 'w'
or 'wt'
rather than True
or False
.
openmode = IS_PY2 and 'w' or 'wt'
openkwargs = IS_PY2 and {} or {'encoding': 'utf-8', 'newline': ''}
Could someone explain the result?
The ternary boolean expression works as:
>>> 2 and 3 or 4
3
>>> 0 and 3 or 4
4
So, this expression:
openmode = IS_PY2 and 'w' or 'wt'
Become in Python 2:
openmode = True and 'w' or 'wt'
Which is equivalent to
openmode = 'w' or 'wt'
So, i gives w
.
Under Python 3, IS_PY2 is False, giving:
openmode = False and 'w' or 'wt'
Which is equivalent to
openmode = False or 'wt'
Giving wt
.
All of this is to specify explicitely that the openmode is for text files, not binary, which is indicated by w
in Python2 and wt
in Python3.
While the Python3 t
mode is the default one, this is not necessary to precise it.
See this answer about wt
mode.
Finally, i think that the following is much more readable:
openmode = 'w' if IS_PY2 else 'wt'
And this one, much more simple:
openmode = 'w'
The and
and or
operators don’t simply perform a boolean operation on their operands, giving a boolean result. The result they give is always one of their operands. These operators evaluate from left to right, with and
having a higher precedence than or
, and they short-circuit, meaning that they stop evaluating their operands as soon as possible.
In pure boolean logic, False and x
is False
, no matter what x
is, so there’s no need to examine x
. The Python expression False and x
will give a result of False
and it will not attempt to evaluate x
. Thus False and some_function()
will not call some_function()
.
Similarly, True and x
in pure boolean logic will have the same truth value as x
, i.e., if x
is True
then True and x
is True
, otherwise its False
.
But the Python and
operator can handle arbitrary operands.
In a and b
if a
is false-ish, then b
won’t be evaluated and the result will be a
. If a
is true-ish, then b
will be evaluated and become the result.
Here’s a short demo, using Python 2:
print False and 'boolean'
print 0 and 'integer'
print '' and 'string'
print [] and 'list'
print
print True and 'boolean'
print 7 and 'integer'
print 'a' and 'string'
print [42] and 'list'
print
print True and False
print True and 0
print True and ''
print True and []
print
output
False
0
[]
boolean
integer
string
list
False
0
[]
(Those blank lines between 0
and []
are where the empty string is getting printed).
Similar considerations apply to the or
operator.
In pure boolean logic, True or x
is True
, no matter what x
is so if the first part of an or
expression is True-ish we don’t need to evaluate the second part. And False or x
has the truth value of x
.
print False or 'boolean'
print 0 or 'integer'
print '' or 'string'
print [] or 'list'
print
print True or 'boolean'
print 7 or 'integer'
print 'a' or 'string'
print [42] or 'list'
print
print False or False
print False or 0
print False or ''
print False or []
print
output
boolean
integer
string
list
True
7
a
[42]
False
0
[]
As I said earlier, these operators are evaluated left to right, and we can chain them if we want. Here are the “classic” cases:
print True and 'yes' or 'no'
print False and 'yes' or 'no'
Those statements are equivalent to
print (True and 'yes') or 'no'
print (False and 'yes') or 'no'
output
yes
no
That construction was common in early versions of Python. These days, it’s far more common to see an if
expression:
print 'yes' if True else 'no'
print 'yes' if False else 'no'
Which is generally considered to be more readable than the ternary expression using and
and or
. Also, a and b or c
is not equivalent to b if a else c
if b
is false-ish.
However, it’s still important to understand how this ternary and ... or
thing works, especially if you need to read or maintain older code. And some old Pythonistas still prefer the and ... or
form, as it’s slightly shorter even if it is a little bewildering when you don’t understand how it works. 🙂