Python Notes
- Basics
- Strings
- Numbers
- Lists
- Tuples
- Dictionaries
- Sets
- Input
- Functions
- OOP
- Random
- Files
- Exceptions
- Testing
- Install Packages
- PEP8
- Virtual Environments
- Decorators
- Generators
- Miscellaneous
- References
Basics
-
If-elif-else
-
dir(random)
prints all the attributes of the random module. -
help(random.randint)
gives the help. -
Everything in Python is an object. Python is object-based.
-
type()
gives the type of the object. -
bool()
returns False if it’s False/0/empty/None; it returns True if it’s not empty. -
Generator is surrounded by parentheses: (it produces one item at a time, unlike listcomp that produces all data at once).
1 2
for i in (x * 3 for x in [1,2,3,4,5]): print(i)
-
is
checks if the two variables refer to the same object.if a is None:
Strings
-
Change Case:
.title()
.upper()
.lower()
-
f-strings are preferred.
1 2
name = 'foo' print(f'The name is {name}.')
-
Stripping Whitespace:
.lstrip()
.rstrip()
.strip()
-
join
: joins the elements of an iterable (list/tuple/dictionary) into a single string:1 2
A = ['A', 'B', 'C'] x = '#'.join(A) # A#B#C
-
Multiline string (’ and ’ are equivalent):
1 2 3 4
a = ''' First line second line third line '''
-
string to list:
string.split('delimiter')
1 2
my_string = 'Hello World' my_list = my_string.split(' ') # ['Hello', 'World']
-
Be consistent with single and double quotes. For triple-quoted strings, always use double quotes.
Numbers
-
We can group digits using underscores to make large numbers more readable:
1 2 3
universe_age = 14_000_000_000 print(universe_age) # Output: 14000000000
-
Multiple Assignment:
x, y, z = 1, 2, 3
-
Constants: no-built in constants, use uppercase as a convention (like in C/C++):
MAX = 100
-
Exponential: Use **:
a = 2 ** 3
-
range():
range(1, 11, 2)
is 1, 3, 5, 7, 9
Lists
-
Adding:
- Insert:
A.insert(0, foo)
- Append:
A.append(foo)
- Concatenate:
A += B
- Insert:
-
Removing:
- By index
- del:
del A[0]
- pop():
last = A.pop()
first = A.pop(0)
- del:
- By value
- remove():
A.remove('foo')
- remove():
- By index
-
Organizing:
- Sorting (Alphabetically):
A.sort()
A.sort(reverse=True)
print(sorted(A))
- Reversing (Chronologically):
A.reverse()
- Sorting (Alphabetically):
-
min(A) max(A) sum(A)
-
List Comprehensions:
A = [a ** 2 for a in range(1,3)]
A = [1,4]
foo = [k for k,v in foo_dictionary.items()]
-
Slicing (not end-inclusive):
A = [0, 1, 2, 3, 4]
print(A[1:4])
gives 1,2,3print(A[:4])
is equivalent toprint (A[0:4])
print(A[1:])
is equivalent toprint (A[1:5])
print(A[-3:])
is equivalent toprint (A[2:])
print(A[0:5:2])
orprint(A[::2])
gives every second letter.print(A[::-1])
prints backwards.- Slicing is nondestructive, while list methods change the state of a list.
-
Copying:
B = A[:]
(full slicing) instead ofB = A
. OrB = A.copy()
. -
Checking existence:
1 2 3 4 5
A = [1,2,3,4,5] if 1 in A: print('1 in A') if 6 not in A: print('6 not in A')
-
With while loops:
1 2 3
while A: # while A is not empty while 1 in A: # while there is 1 in A
-
enumerate()
:1 2
for i, item in enumerate(a): print(f'{i} - {item}')
Tuples
-
Tuples can’t be modified:
A = (1, 2)
A[0] = 3
doesn’t work. (Immutable list) -
But tuples can be reassigned:
A = (1, 2)
A = (3, 2)
works. -
For a single-object tuple like
t = ('Python')
, it becomes a string. But if we add a comma, it becomes a tuplet = ('Python',)
Dictionaries
-
Basic usage:
1 2 3 4
A = {'language': 'python', 'age': 19} print(A['language']) A['height'] = 190 # adding a new pair A['language'] = 'C++' # modifying
-
get() (When not sure if the key exists):
print (A.get('weight', 'no weight assigned'))
(preferred) -
Looping through:
1 2 3 4 5 6 7 8
for k, v in A.items(): # keys and values for k in A.keys(): # equivalent to the line below for k in A: # since looping through the keys is default for k in sorted(A.keys()) # sorted for v in A.values(): # just values for v in set(A.values()): # unique values
-
Lists and dictionaries can be nested into each other or themselves.
-
Check for membership with
in
andnot in
:1 2 3 4
if 'bananas' in fruits: fruits['bananas'] += 1 else: # not in fruits['bananas'] = 1
-
setdefault()
to avoid KeyError:x = fruits.setdefault(fruit, 0)
(it returns the value. If it doesn’t exist, insert the key with the specified value.) -
import pprint
pprint.pprint()
pretty-print for complex data structures. -
Dictionary comprehension:
1 2 3
flights = {k: v.title() for k, v in flights.items() if v == 'foo'} # optional filter
Sets
-
Basic usage:
1 2 3 4 5 6
empty_set = set() languages = {'python', 'C++', 'C', 'python'} print(languages) # Output {'python', 'C++', 'C'} word = 'hello' wordSet = set(word)
-
.union()
,.difference()
,.intersection()
-
Set comprehension:
foo = {v for v in vowels}
Input
- Reading an int:
n = int(input('Please input a number: '))
Functions
-
Optional keyword arguments that avoid confusion:
1 2 3 4 5
def minus(a, b): return (a - b) print(minus(a=1, b=2)) # is equivalent to the line below print(minus(b=2, a=1))
-
Default values for the parameters can be added:
1 2 3 4
def minus(a, b = 0): return (a - b) print(minus(1))
-
We can make an argument optional by using None:
1 2 3 4 5
def build_person(first, last, age=None): person = {'first_name': first, 'last_name': last} if age: person['age'] = age return person
-
To prevent a function from modifying a list, pass the list with full slicing:
foo(A[:])
-
Variadic functions:
1 2 3 4 5 6
def print_languages(*languages): # The * makes an empty tuple and packs any value it receives for language in languages: print(f'- {language}') # more generically, *args # **kwargs for key-value pairs # * works on the way in as well: *foo expands the list foo to individual arguments
-
Importing:
1 2 3 4 5 6 7
from module_name import func_1, func_2, func_3 # importing multiple functions from module_name import func_1 as f # alias import module_name as m # alias from module_name import * # all functions # We must import everything at the beginning of each file # Searching in 1. wd 2. site-packages 3. standard library locations
-
Add docstrings for functions:
1 2 3
def foo(): """This is a docstring.""" return 'bar'
-
Use annotations/type hints to improve the docs: (optional and informational only)
1 2 3
def foo(word: str) -> str: """Take a string and return a string.""" return word
-
Python supports both pass-by-value and pass-by-reference. Variables in Python are object references. If there’s only assignment, the original object won’t change.
OOP
-
Example:
1 2 3 4
class Person: def __init__(self, name, age): self.name = name self.age = age
-
Default value:
1 2 3
class foo: def __init__(self, value): self.value = 0
-
Inheritance:
1 2 3 4 5 6
from foo import Foo class Foo(Bar): # Bar is the parent class def __init__(self, value): super().__init__(value) # superclass # we can also override a method by redefining it in the child class.
-
Conventions:
- Capitalize the first letter of each word, without underscores. Instances and module names use underscores and are in lowercase.
Random
-
randint (end inclusive):
1 2
from random import randint print(randint(1,6))
-
randrange (end exclusive)
-
choice:
1 2 3
from random import choice A = [1,2,3,4,5,6] print(choice(A))
Files
-
Reading an entire file:
1 2 3 4 5 6 7
with open('file.txt') as file_obj: # 'r' is the default mode contents = file_obj.read() # lines = file_obj.readlines() -> a list of lines # for line in lines: #print(line.rstrip()) print(contents.rstrip()) # Removing the additional \n # no need to close, python will do it automatically
-
Reading line by line:
1 2 3
with open('file.txt') as file_obj: for line in file_obj: print(line.rstrip())
-
Writing:
1 2 3
# python only writes strings, use str() if necessary with open('file.txt', 'w') as file_obj: # 'a', 'r+' file_obj.write('Python.')
Exceptions
-
Basic try-except-else:
1 2 3 4 5 6
try: print(1/0) except ZeroDivisionError as err: print('Can't divide by 0.', str(err)) else: # optional print('Success')
-
Failing silently using pass:
1 2 3 4 5 6 7
try: with open('file.txt') as file_obj: content = file_obj.read() except FileNotFoundError: pass else: print(len(content.split())) # word count
-
Raise an error:
raise FileNotFoundError('foo')
Testing
-
Basic unit testing:
1 2 3 4 5 6 7 8 9 10
import unittest from file import function_1: class Func1Test(unittest.TestCase): def test(self): result = function_1(value) self.assertEqual(result, foo) if __name__ = '__main__': unittest.main()
-
setUp() method can be used to test a class
Install Packages
-
sudo python3 -m pip install foo
(foo could also be a tar.gz) installs to our site-packages. -
We can generate a distribution package by using setuptools.
PEP8
-
pip3 install pycodestyle
pycodestyle --show-source --show-pep8 foo.py
tests for PEP8 compliance. -
For multiline with binary operators, break before the operators:
1 2 3 4 5
income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
-
Surround functions and classes with two blank lines. Methods in a class are surrounded by a single blank line.
-
Imports should be on separate lines.
-
Be consistent with returns.
return None
if any return statement returns an expression.1 2 3 4 5 6 7 8 9 10
def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
-
Use methods instead of modules:
1 2 3 4 5 6 7
if foo.startswith('bar'): # or endswith # is better than if foo[:3] == 'bar' if isinstance(obj, int): # is better than if type(obj) is type(1):
-
Use the fact that empty sequences (strings/lists/sets/etc.) are false:
1 2 3 4 5
if not seq: if seq: # are better than if len(seq): if not len(seq):
-
pip3 install autopep8
autopep8 --in-place foo.py
auto-formats the code. -
Prefix with an underscore for ‘private’ properties/methods. (As a convention but not a protection.)
-
Add parentheses for continuing a long line of code.
Virtual Environments
-
Create and activate with virtualenv:
1 2 3 4 5 6 7
cd foo virtualenv --python python3 my_venv source my_venv/bin/activate pip install numpy pip freeze > requirements.txt pip install -r requirements.txt deactivate
Decorators
-
A decorator is a function that takes another function as an argument and replaces it with a new modified function.
-
Creation:
1 2 3 4 5 6 7
def identity(f): return f @identity def foo(): return 'bar' # This same as foo = identity(foo)
-
Static methods:
1 2 3 4 5 6
class Pizza: @staticmethod def mix_ingredients(x, y): return x + y three = Pizza.mix_ingredients(1, 2)
-
Class methods (used for factory methods. which instantiate objects using a different signature other than
__init__
):1 2 3 4 5 6 7
class Pizza: def __init__(self, ingredients): self.ingredients = ingredients @classmethod def from_fridge(cls, fridge): return cls(fridge.get_cheese() + fridge.get_vegetables())
-
Abstract methods:
1 2 3 4 5 6 7 8 9 10 11
class Pizza: @staticmethod def get_radius(): raise NotImplementedError # Or make the class abstract (abc: abstract base class) from abc import ABC, abstractmethod class BasePizza(ABC): @abstractmethod def get_radius(self): """Method that should do something."""
Generators
-
Generators return the value but save the stack reference, which will be used to resume the execution when
next()
is called. -
Example:
1 2 3 4 5 6 7 8 9
def my_generator(): yield 1 yield 2 yield 'a' g = my_generator() next(g) # 1 next(g) # 2 next(g) # a
-
range()
returns a generator, which generates the values on the fly. It’s good for handling large data set.
Miscellaneous
-
We can’t use mutable types as dictionary keys since their hash will change. Strings are not mutable. And use tuples over lists when they are meant to be immutable.
-
Use list comprehensions over loops.
-
Python requires an
__init__.py
file for the directory to be considered a submodule.