View on GitHub


Python 3 notes cheatsheet, focusing on fundamentals and useful interview tips

Table of Contents


Built-in Types

In this section I have included information on the more basic built-in types. For information on more specialized built-in types, check out the Python documentation

Boolean Types

class 'bool'

By default, an object is considered True unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero. Here are most of the built-in objects considered False:

Numeric Types

class 'int'
class 'float'
class 'complex'

Integers have unlimited precision. Floating point numbers are usually implemented using double in C, and are therefore system-dependent. Complex numbers have a real and imaginary part, which can be accessed using z.real and z.imag, respectively. Complex numbers must include j appended to a numeric literal (0j is acceptable for when you want a complex value with no imaginary part).

The standard libarary includes additional numeric types, Fractions which hold rationals, and Decimals which hold floating-point numbers with user-definable precision.

Sequence Types

Immutable sequences have support for the hash() built-in, while mutable sequences do not. This means that immutable sequences can be used as dict keys or stored in set and frozenset instances, while mutable sequences cannot.

Mutable Sequences

class 'list'
class 'bytearray

bytearray objects are a mutable counterpart to bytes objects.

Immutable Sequences

class 'tuple'
class 'range'
class 'str'
class 'bytes'

bytes objects are sequences of single bytes. The syntax for bytes literals is largely the same as that for string literals, except that a b prefix is added:

Only ASCII chars are permitted in bytes literals.
bytes objects actually behave like immutable sequences of integers, with each value restricted to 0 <= x < 256.

bytes objects can be created in several ways:

Set Types

class 'set'
class 'frozenset'

set is mutable, while frozenset is immutable.
Note that since frozenset is immutable, it must be entirely populated at the moment of construction. It cannot use the literal curly brace syntax that ordinary set uses, as that syntax is reserved for set.

Instead, use frozenset([iterable]).

Mapping Types

class 'dict'

See the Dictionaries section for more info.



Dictionary Iteration

Get w/ default value if key not in dict:

my_dict[k] = my_dict.get(k, 0) + 1; # get retrieves value for k, or 0 if k not in dict

Iterating a dict iterates only the keys:

for k in my_dict:  # k will be each key, not each key-value pair

Testing membership: if k in dict: ...

To get actual key-value pairs at the same time:

for k,v in my_dict.items():

applies to comprehensions as well: new_d = {k: v+1 for k,v in d.items()}

Dictionary Sorting

It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.



List Comprehensions

General Syntax:

[<expression> for item in list if conditional]

is equivalent to:

for item in list:
    if conditional:

Note how the order of the for and if statements remains the same. For example,

for row in grid:
    for x in row:

is the same as

[<expression> for row in grid for x in row]

List Initialization

Can use comprehensions:

my_list = [i for i in range(10)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2-D list (list of lists):

my_list = [[] for i in range(3)] # [[], [], []]

This is useful for a “visited” grid of some kind (common in Dynamic Programming problems):

visited = [[0 for i in range(len(grid[0]))] for j in range(len(grid))]

BE CAREFUL when initializing a matrix. Do this:

my_list = [[None] * n for i in range(n)]

NOT this:

my_list = [[None] * n] * n

The latter method makes copies of the reference to the original list, thus any modification to one row will change the other rows in the same way. The first method does not do this.

A list can be created from a string using list(my_str) We can apply a filter as well:

my_list = list(c for c in my_str if c not in ('a', 'c', 'e'))

List Reversal

List Sorting

By default, these methods will sort the list in ascending order. For descending order, we can supply the arg reverse=True to either of the aforementioned methods.

We can also override the key for sorting by supplying the key arg. For example, if we have a list of tuples and we want to use the second item as the key:

 list1 = [(1, 2), (3, 3), (4, 1)] 
 list1.sort(key=lambda x: x[1]) # list1 is now [(4, 1), (1, 2), (3, 3)]

Additionally, if we want to sort in descending order:

 list1.sort(key=lambda x: x[1], reverse=True) # list1 is now [(3, 3), (1, 2), (4, 1)]

When using sorted() it works the same, except we supply the list as the first arg:

 list2 = sorted(list1, key=lambda x: x[1], reverse=True)



From List

my_list = ['te', 's', 't', '1', '2', '3', '_']
s = ''.join(my_list) # "test123_"
s2 = ''.join(c for c in my_list if c.isalnum()) # "test123"

String Constants

Python has a lot of useful string constants. A few of them are shown below. For a complete list, see the documentation

e.g. if d in string.digits: ...


Returns True if a string consists only of alphanumeric characters.

s = "test123"
s.isalnum() # True


Return a list of the words in the string, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done (thus, the list will have at most maxsplit + 1 elements). If maxsplit is not specified or -1, then there is no limit on the number of splits (all possible splits are made).

str.split(sep=None, maxsplit=-1)
'1,2,3'.split(',')             # ['1', '2', '3']
'1,2,3'.split(',', maxsplit=1) # ['1', '2,3']
'1,2,,3,'.split(',')           # ['1', '2', '', '3', '']


Returns copy of string without surrounding whitespace, if any.

s = "   test "
s.strip() # "test"

str() vs repr()

See this GeeksForGeeks article for more info.



In Python, an iterator is an object with a countable number of values that can be iterated upon. An iterator is an object which implements the iterator protocol, consisting of __iter__() and __next__().
The __iter__() method returns an iterator on the object, and the __next__() method gets the next item using the iterator, or raises a StopIteration exception if the end of the iterable is reached.

Iterator vs Iterable

Lists, tuples, dictionaries, and sets are all iterable objects. They are iterable containers which you can get an iterator from. All these objects have a __iter__() method which is used to get an iterator:

mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)

print(next(myit)) # apple
print(next(myit)) # banana
print(next(myit)) # cherry
print(next(myit)) # raises StopIteration exception

Note – next(obj) is the same as obj.__next__().

How for loop actually works

The for loop can iterate any iterable.
The for loop in Python is actually implemented like so:

iter_obj = iter(iterable) # create iterator object from iterable

# infinite loop
while True:
        element = next(iter_obj) # get the next item
        # do something with element
    except StopIteration:

So, internally, the for loop creates an iterator object by calling iter() on the iterable, and then repeatedly calling next() until a StopIteration exception is raised.

Creating an Iterator

Here is an example of an iterator that will give us the next power of two in each iteration.

class PowTwo:
    """Class to implement an iterator of powers of two"""

    def __init__(self, max = 0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
            raise StopIteration

Now we can use it as follows:

>>> a = PowTwo(4)
>>> i = iter(a)
>>> next(i)
>>> next(i)
>>> next(i)
>>> next(i)
>>> next(i)
>>> next(i)
Traceback (most recent call last):

Or, alternatively, using a for loop:

>>> for i in PowTwo(5):
...     print(i)


Functional Iteration

For some good explanations and examples for the following functions, see here.

Note that map() and filter() both return iterators, so if you want a list, you need to use list() on the output. However, this is typically better accomplished with list comprehensions or for loops for the sake of readability.


map() applies a function to all the items in a list.

map(function_to_apply, list_of_inputs)

For example, the following code:

items = [1, 2, 3, 4, 5]
squared = []
for i in items:

can be accomplished more easily with map():

items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))


filter() creates a list of elements for which a function returns True.

Here’s an example:

number_list = range(-5, 5)
less_than_zero = list(filter(lambda x: x < 0, number_list))
print(less_than_zero) # [-5, -4, -3, -2, -1]


reduce() is used to perform a rolling computation on a list.

Here’s an example:

from functools import reduce
number_list = [1, 2, 3, 4]
product = reduce((lambda x, y: x * y), number_list) # output: 24

Often times, an explicit for loop is more readable than using reduce(). But if you’re trying to flex in an interview, and the problem calls for it, it could be a nice way to subtly show your understanding of functional programming.



A decorator is a function returning another function, usually applied as a function transformation using the @wrapper syntax. This syntax is merely syntactic sugar.

The following two function definitions are semantically equivalent:

def f(...):
f = staticmethod(f)

def f(...):


Transform a method into a class method. A class method receives the class as implicit first argument, just like how an instance method receives the instance. To declare a class method:

class C:
    def f(cls, arg1, arg2, ...):

A class method can be called either on the class (like C.f()) or on an instance (like C().f()). The instance is ignored except for its class. If a class method is called for a derived class, the derived class object is passed as the implied first argument.

Note that class methods are not the same as C++ or Java static methods. If you want those, see @staticmethod.


Transform a method into a static method. A static method does not receive an implicit first argument. To declare a static method:

class C:
    def f(arg1, arg2, ...):

A static method can be called either on the class (like C.f()) or on an instance (like C().f()). Static methods in Python are similar to those found in Java or C++.


Return a property attribute. Usage:

property(fget=None, fset=None, fdel=None, doc=None)

fget is a function for getting an attribute value. fset is a function for setting an attribute value. fdel is a function for deleting an attribute value. doc creates a docstring for the attribute.

The following is a typical use case for defining a managed attribute x:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

Or, equivalently:

class C:
    def __init__(self):
        self._x = None

    def x(self):
        """I'm the 'x' property."""
        return self._x

    def x(self, value):
        self._x = value

    def x(self):
        del self._x

If c is an instance of C, then c.x will invoke the getter; c.x = value will invoke the setter; and del c.x the deleter.

If doc is not provided, the property will copy fget’s docstring, if it exists. Thus, it is straightforward to create read-only properties with the @property decorator:

class Parrot:
    def __init__(self):
        self._voltage = 100000

    def voltage(self):
        """Get the current voltage."""
        return self._voltage

The @property decorator turns the voltage() method into a “getter” for a read-only attribute with the same name, and it sets the docstring for voltage to “Get the current voltage.”

For more information, check out the documentation and this Programiz article.



Generators are simpler ways of creating iterators. The overhead of creating __iter__(), __next__(), raising StopIteration, and keeping track of state can all be handled internally by a generator.

A generator is a function that returns an object (iterator) which we can iterate over, one value at a time.

Using yield

To create a generator, simply define a function using a yield statement.

A function containing at least one yield statement (it may contain other yield and return statements) becomes a generator.

Both yield and return return some value from a function. The difference is that, while a return statement terminates a function entirely, yield pauses the function, saving its state and continuing from where it left off in successive calls.

Once a function yields, it is paused and control is transferred back to the caller. Local variables and their states are remembered between successive calls. When the function terminates, StopIteration is raised automatically on further calls.

Below is a simple generator example, for the sake of demonstrating how generators work.

def my_gen():
    n = 1
    print('This is printed first')
    yield n

    n += 1
    print('This is printed second')
    yield n
# Without for loop:
a = my_gen()
next(a) # 'This is printed first'
next(a) # 'This is printed second'
next(a) # Traceback ... StopIteration

# With for loop:
for item in my_gen():

Below is a more typical example. Generators often use loops with a suitable terminating condition.

def reverse(my_str):
    for i in range(len(my_str) - 1, -1, -1):
        yield my_str[i]
for char in reverse("hello"):
    print(char) # prints each char reverse on a new line

Note that the above example works not just with strings, but also other kinds of iterables.

Generator Expressions

Generator expressions can be used to create an anonymous generator function. The syntax is similar to that of list comprehensions, but uses parentheses instead of square brackets. However, while a list comprehension produces the entire list, generator expressions produce one item at a time.

Generator expressions are kind of lazy, producing items only when asked for. For this reason, using a generator expression is much more memory efficient than an equivalent list comprehension.

items = [1, 3, 6]
item_squared = (item**2 for item in items)
print(next(item_squared)) # 1
print(next(item_squared)) # 9
print(next(item_squared)) # 36
next(item_squared) # StopIteration

Generator expressions can be used inside function calls. When used in such a way, the round parentheses can be dropped.

sum(x**2 for x in items) # 46
max(x**2 for x in items) # 36


Other Useful Built-in Functions

For a complete list of built-ins in Python 3, see the documentation.


Returns the absolute value of a number, either an integer or floating point number. If the argument is a complex number, its magnitude is returned.




any() takes any iterable as an argument and returns True if at least one element of the iterable is True.

any([1, 3, 4, 0])   # True
any([0, False])     # False
any([0, False, 5])  # True
any([])             # False

any("This is good") # True
any("0")            # True
any("")             # False

See here for more info.

Check if any tuples contain a negative value:

if any(x < 0 or y < 0 for (x, y) in list_ranges): ...



all() takes any iterable as an argument and returns True if all the elements of the iterable are True.

all([1, 3, 4, 5])  # True
all([0, False])    # False
all([1, 3, 4, 0])  # False
all([0, False, 5]) # False
all([])            # True

all("This is good") # True
all("0")            # True
all("")             # True

See here for more info.

Check if all elements of a list are x:

if all(c == x for c in alst): ...


Returns the string representing a character whose Unicode code point is the integer passed.

For example, chr(97) returns the string a, while chr(8364) returns the string .

This is the inverse of ord().



enumerate(iterable, start=0)

Returns an enumerate object. iterable must be a sequence, iterator, or some object which suports iteration. The __next__() method of the iterator returned by enumerate() returns a tuple containing a count (from start which defaults to zero) and the values obtained from iterating over the iterable.


seasons = ['Spring', 'Summer', 'Fall', 'Winter']
list(enumerate(seasons))              # [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
list(enumerate(seasons, start=1))     # [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

This is equivalent to:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1


Gets input from the user. Usage:



>>> s = input('-> ')  
-> Monty Python's Flying Circus
>>> s  
"Monty Python's Flying Circus"

If the prompt arg is present, it is written to stdout without a trailing newline.



isinstance(object, classinfo)

Returns true if the object argument is an instance of the classinfo argument, or of a (direct, indirect, or virtual) subclass thereof. Returns false otherwise.

If classinfo is a tuple of type objects, return true if object is an instance of any of any of these types.


Return the length of an object. The argument may be a sequence (e.g. string, bytes, tuple, list, or range) or a collection (e.g. dictionary, set, frozen set).


Returns the max item in an iterable, or the max of multiple arguments passed.


Returns the min item in an iterable, or the min of multiple arguments passed.


Given a string representing one Unicode character, return an integer representing the Unicode code point of that character.

For example, ord('a') returns the integer 97. ord('€') (Euro sign) return 8364.

This is the inverse of chr().



pow(x, y[, z])

Return x to the power y; if z is present, return x to the power y, modulo z (computed more efficiently than pow(x, y) % z).
pow(x, y) is equivalent to x**y.



type(name, bases, dict)

With one argument, return the type of object. The return value is a type object and generally the same object as returned by object.__class__.


x = 5
type(x)  # class 'int'

The isinstance() function is recommended for testing the type of an object, since it accounts for subclasses.


Common Gotchas

Nested List Initialization

When creating a list of lists, be sure to use the following structure:

my_list = [[None] * n for i in range(n)]

Read the section on list initialization to see why.

Mutable Default Arguments

If we try to do something like def f(x, arr=[]) this will most likely create undesirable behavior. Default arguments are resolved only once, when the function is first defined. The same arg will be used in successive function calls. In the case of a mutable type like a list, this means that changes made to the list in one call will be carried over in successive calls. Instead, consider doing:

def f(x, arr=None):
    if not arr: arr = []