Editor's note: This guide is part of a series on useful Python tricks. Read more about the series and find links the other guides here.
In this guide, we will learn some basic tricks in Python, including the most common advanced Python tricks. Each Python trick is explained in two steps:
If a
is the truthy value, return a
. Otherwise, return b
.
Here is the falsy values list from the official document:
None
and False
.0
, 0.0
, 0j
, Decimal(0)
, Fraction(0, 1)
.''
, ()
, []
, {}
, set()
, range(0)
.1a, b = 0, 42
2a or b
3# output: 42
4
5a, b = '', 0
6a or b
7# output: 0
In a linked list assignment, if at most one of the two candidate nodes is not None
, we can use or
expression.
1"""connect with a non-empty linked list"""
2cur.next = l1 or l2
If a
is the falsy value, return a
. Otherwise, return b
.
1a, b = 1, 2
2a and b
3# output: 2
4
5a, b = '', 'abc'
6a and b
7# output: ''
We can use this mechanism to call a function before assignment. Just make sure that the left expression is always True
.
1"""add current element to list before assignment"""
2last = not arr.append(x) and arr[-1]
Lazy evaluation is an evaluation strategy that delays the evaluation of an expression until its value is needed.
Here we talk about the lazy evaluation mechanism applied to expression, such as a or b
, a and b
, a if condition else b
. Once the statement is satisfied, the rest of the expression will be skipped. Specific to the following examples, expression b
will not be executed. We can use this mechanism to do conditional execution in an expression. See more information in the "list comprehension with break" example of the Python Tricks - Black Magic - Part 2 guide.
We will further discuss the lazy evaluation technique applied to the generator in the Python Tricks - Iterable guide.
Construct a temporary tuple for a specific purpose. Trade memory for more concise code. This can be extended to build other collection types.
1"""build in condition"""
2if elem in (elem1, elem2): # equals to: if elem == elem1 or elem == elem2
3
4"""build result"""
5return [dp[-1], -1][dp[-1] == float('inf')] # equals to if-else, but more concise
6
7"""build 4-neighbors as range"""
8for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):
bool
type is a subclass of int
. As an integer, the value of True
is 1 and False
is 0.
1int(True), int(False)
2# output: (1, 0)
We can use bool
to participate in operations.
Used in sum
: count the number of satisfied conditions.
1"""used in sum"""
2equal_count = sum(a == b for a, b in zip(arr1, arr2))
Used as an index: construct a temporary tuple and use bool
expression as an index.
1# L236: find the lowest common ancestor in the binary search tree
2def lowest_common_ancestor(root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
3 while (root.val - p.val) * (root.val - q.val) > 0:
4 """used as index"""
5 root = (root.left, root.right)[p.val > root.val] # equals to if-else, seems more clearly this way
6 return root
Used in list initialization and slice:
1# a strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down)
2# L247: find all strobogrammatic numbers that are of length = n
3def find_strobogrammatic(n: int) -> List[str]:
4 """used in list initialization"""
5 # if nums is odd, the middle digit must be in '018'
6 nums = n % 2 * list('018') or ['']
7 while n > 1:
8 n -= 2
9 """used in slice"""
10 # here use to deal with number can not start with '0'
11 nums = [a + num + b for a, b in '00 11 88 69 96'.split()[n < 2:] for num in nums]
12 return nums
In C++ / Java, you can achieve this with the built-in grammar condition ? true_expression : false_expression
. Python has several of its own ways to achieve this. A variety of solutions provide different ideas.
This section is summarized from Does Python have a ternary conditional operator? and Best way to do ternary conditionals in Python < 2.5 on Stack Overflow. Refer to them if you need.
This is the most direct way, which is supported since version 2.5. The expression syntax is:
1"""conditional operator"""
2true_expression if condition else false_expression
We can use the ternary operator to find min/max element in pairs or triplets. The alternative is to use the min
/max
function.
1"""find maximum in pairs"""
2a, b = 10, 20
3max_val = a if a > b else b
4# output: 20
5
6"""find mininmum in triplets"""
7a, b, c = 10, 20, 5
8min_val = a if a < b and a < c else (b if b < c else c)
9# output: 5
For versions prior to 2.5, we can use the boolean expression trick that we discussed before:
1"""correct way by encapsulation with tuples"""
2"""(False,) is truthy value"""
3(condition and (true_expression,) or (false_expression,))[0]
4
5"""more concise and faster way with limit"""
6"""should make sure true_expression is never falsy"""
7condition and true_expression or false_expression
However, this method may create poor readability for some and be slower than before.
This solution uses value of boolean and build tuple tricks.
1"""tuple index"""
2"""for safety we explicitly test for truthiness by bool()"""
3(false_expression, true_expression)[bool(condition)]
4
5"""dict index"""
6{True: true_expression, False: false_expression}[bool(condition)]
7
8"""lambda version supports lazy evaluation"""
9(lambda: false_expression, lambda: true_expression)[bool(condition)]()
Note that the first two solutions always evaluate everything, whereas the if/else construct obeys lazy evaluation. They are not equal in the strict sense. The third one uses the lambda
technique to solve this problem.
Here is an elegant Pythonic solution. It is packed as a function based on the boolean index technique solution. It looks pretty clean and easy to read for the caller.
1"""pack as function"""
2"""not not x equals to bool(x)"""
3def _if(condition):
4 return lambda false_expression: lambda true_expression: \
5 [delay(true_expression), delay(false_expression)][not not condition]()
6
7"""unified as callable"""
8def delay(f):
9 if callable(f): return f
10 else: return lambda: f
11
12# example
13a, b = 10, 20
14min_val = _if(a < b) (a) (b)
15# output: 10
Chained operations make the code more concise and reduce the possibility of errors.
In Python, assignment statements are not expressions and thus do not have a value. Instead, chained assignments are a series of statements with multiple targets for a single expression. The assignments are executed left-to-right.
1x = y = f()
is equivalent to
1temp = f()
2x = temp # assign from left to right
3y = temp
They all have the same values when assigning a mutable value.
1import random
2x = y = random.random()
3x == y
4# output: True
5
6x = random.random()
7y = random.random()
8x == y
9# output: False
A typical use case is for initialization, such as the initialization of a linked list.
1"""initialize dummy node and assign to cur"""
2dummy = ListNode(0)
3cur = dummy.next = head
Chained Comparison is a nice syntactic sugar in Python to simplify expressions.
1a = b = 1
2
3a == b == 1
4# output: True
5
6a == b != 2
7# output: True
8
90 <= a < 4
10# output True
1"""short for a > 0 and b >= 0"""
2if a > 0 <= b:
3
4"""chained comparison in binary search"""
5if nums[lo] < target < nums[mid]:
6 hi = mid - 1
7
8"""check matrix boundary in a unified way"""
9for i in range(len(matrix)):
10 for j in range(len(matrix[0])):
11 # construct neighbor tuple
12 for I, J in (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1):
13 # although add some unnecessary checks, expression is more concise
14 if 0 <= I < len(matrix) and 0 <= J < len(matrix[0]):
15 process_neighbor_logic(matrix[I][J])
In this guide, we have learned many basic Python tricks, such as advanced boolean usages, build tuple, ternary operator, and chained operator. I hope some of them will be useful for you.
In Python Tricks - Basic - Part 2, we will continue to learn about other basic Python tricks.
You can also download an example notebook, basic.ipynb, from Github.
This guide is one of a series of Python tricks guides:
I hope you enjoyed it. If you have any questions, you're welcome to contact me at [email protected].