Python tricks

1. Introduction

This section lists useful Python tricks that may not be obvious to the average Python user.

2. Tricks

2.1. Python built-ins

There are some Python builtin functions that are useful to know about.

2.1.1. Numbers

2.1.1.1. abs()

abs(x)

Returns absolute value of a number x.

2.1.1.2. bin()

bin(x)

Convert an integer number to its binary representation as a string, prefixed with 0b.

The table below has some examples, including equivalent methods using format and format strings.

Expression Result of evaluation
bin(3) '0b11'
bin(-5) '-0b101'
format(14, '#b') '0b1110'
format(14, 'b') '1110'
f'{14:#b}' '0b1110'
f'{14:b}' '1110'

The bin(x), oct(x), and hex(x) functions all behave similarly but for the binary, octal, and hexadecimal bases, respectively.

hex(x)
Convert integer into lower-case hex number with a leading '0x' prefix.
oct(x)
Convert integer into octal number with a leading '0o' prefix.
2.1.1.3. divmod()

divmod(x, y)

Returns a tuple of the reslult of integer division of x by y and remainder. Equivalent to (x // y, x % y).

2.1.1.4. pow()

pow(base, exp, mod=None)

Raise a number base to the exponent exp. The form 3**4 is shorthand for pow(3, 4).

2.1.1.5. round()

round(number, ndigits=None)

From Python 3.11.3 docs: "Return number rounded to ndigits precision after the decimal point. If ndigits is omitted or is None, it returns the nearest integer to its input."

This function doesn't see much use in programming problems because rounding itself is an imprecise operation resulting in the loss of data, and typically loss of data is never a good thing to do.

2.1.2. Strings

2.1.2.1. chr()

chr(i)

Inverse of ord().

Expression Result of evaluation
chr(97) 'a'
chr(8364) '€'
2.1.2.2. ord()

ord(c)

Inverse of chr().

Expression Result of evaluation
ord('a') 97
ord('€') 8364
2.1.2.3. repr()

repr(x)

Return a str version of an object, where the goal is to make it so that entering this verbatim back into Python will result in the same (original) value.

As a memory aid, think of repr as related to REPLs, because typing the result of repr() into Python's REPL (or passing it into eval()) should just work.

2.1.2.4. str()

str(x)

Return a str version of an object, where the goal is to be human-readable (read: open to being machine-unfriendly).

Not often used in practice because of format strings.

2.1.3. Iterables and sequences

2.1.3.1. all()

all(iterable)

Returns True if no element is False (returns True if no elements).

2.1.3.2. any()

any(iterable)

Returns False if no element is True (returns False if no elements).

2.1.3.3. enumerate()

enumerate(iterable, start=0)

Returns an iterable of tuples, where the first element is the index (starting from 0) and the second element is the element in the iterable.

2.1.3.4. filter()

filter(function, iterable)

Removes elements in the given iterable with the given function, if those elements do not return True when passed into the function.

2.1.3.5. len()

len(sequence)

Returns the length of the sequence.

2.1.3.6. map()

map(function, iterable, *iterables)

From Python 3.11.3 docs: "Return an iterator that applies function to every item of iterable, yielding the results. If additional iterables arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the shortest iterable is exhausted."

This is somewhat similar to zip in Haskell, except that a function is involved.

2.1.3.7. max()

Return the largest element of the sequence, or the larger of 2 elements.

2.1.3.8. min()

Return the smallest element of the sequence, or the smaller of 2 elements.

2.1.3.9. next()

next(iterator, default)

From Python 3.11.3 docs: "Retrieve the next item from the iterator by calling its _next_() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised."

2.1.3.10. range()

range(start, stop, step=1)

The Python docs say that range() is not a function, but an immutable sequence. While this is correct, for 99.99% of cases it is used as a function.

2.1.3.11. reversed()

reversed(seq)

Return given sequence seq in opposite (reversed) order. The returned object is an iterator.

2.1.3.12. set()

set(iterable)

Convert given iterable into a set.

2.1.3.13. sorted()

sorted(iterable, /, *, key=None, reverse=False)

Return a new sorted list from the items in iterable.

2.1.3.13.1. Example

Sort tuples by the second element, not the first (default).

def test_sort(self):
    xs = [("a", 9), ("b", 5), ("c", 1)]
    result = sorted(xs, key=lambda x: x[1])
    self.assertEqual(result, [("c", 1), ("b", 5), ("a", 9)])
2.1.3.14. sum()

sum(iterable, /, start=0)

Return the total of all elements in the iterable.

2.1.3.15. zip()

zip(*iterables, strict=False)

From Python 3.11.3 docs: "Iterate over several iterables in parallel, producing tuples with an item from each one."

If you want to write zip(range(3), ['a', 'b', 'c']), instead write enumerate(['a', 'b', 'c']).

2.2. Pythonisms

2.2.1. Indexing strings (or lists)

Python uses 0-based indexing, and you can use this to select any particular element in a string or list.

Negative indices are allowed.

def test_indices(self):
  self.assertEqual('abcdefg'[0], 'a')
  self.assertEqual('abcdefg'[1], 'b')
  self.assertEqual('abcdefg'[-1], 'g')
  self.assertEqual('abcdefg'[-2], 'f')
  self.assertEqual('abcdefg'[-7], 'a')

  # Negative indices do not go on forever.
  with self.assertRaises(IndexError):
    'abcdefg'[-8]

See https://docs.python.org/3/tutorial/introduction.html#text.

2.2.2. Slicing

Whereas indexing returns a single element in a list-like structure, slicing returns a substring (a series of consecutive elements inside the list). The syntax is <start index (included)>:<final index (excluded)>.

def test_slices(self):
  self.assertEqual('abcdefg'[0:2], 'ab')
  self.assertEqual('abcdefg'[2:5], 'cde')

If the start index is omitted, it defaults to 0. If the final index is omitted, it defaults to the length of the string (so it's the same as grabbing all remaining elements).

  self.assertEqual('abcdefg'[:2], 'ab')
  self.assertEqual('abcdefg'[2:], 'cdefg')

Slices indexes that are out of range are handled gracefully (they do not raise an error).

  self.assertEqual('abcdefg'[1000:], '')
  self.assertEqual('abcdefg'[:1000], 'abcdefg')

2.3. Array traversal

2.3.1. Burn the candle at both ends

When iterating through an array, you can use ~i to get the value -i - 1. This is because two's complement states that -i is equal to ~i + 1.

def is_palindrome(s: str) -> bool:
  return all(s[i] == s[~i] for i in range(len(s) // 2))

Time complexity is \(O(n)\) and space complexity is \(O(1)\).

3. Tests

🔗
import unittest

tricks

class Test(unittest.TestCase):
  test_cases

if __name__ == "__main__":
  unittest.main(exit=False)

4. References