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 2for i in (x * 3 for x in [1,2,3,4,5]): print(i) -
ischecks if the two variables refer to the same object.if a is None:
Strings
-
Change Case:
.title().upper().lower() -
f-strings are preferred.
1 2name = '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 2A = ['A', 'B', 'C'] x = '#'.join(A) # A#B#C -
Multiline string (’ and ’ are equivalent):
1 2 3 4a = ''' First line second line third line ''' -
string to list:
string.split('delimiter')1 2my_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 3universe_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 5A = [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 3while A: # while A is not empty while 1 in A: # while there is 1 in A -
enumerate():1 2for i, item in enumerate(a): print(f'{i} - {item}')
Tuples
-
Tuples can’t be modified:
A = (1, 2)A[0] = 3doesn’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 4A = {'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 8for 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
inandnot in:1 2 3 4if '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 pprintpprint.pprint()pretty-print for complex data structures. -
Dictionary comprehension:
1 2 3flights = {k: v.title() for k, v in flights.items() if v == 'foo'} # optional filter
Sets
-
Basic usage:
1 2 3 4 5 6empty_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 5def 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 4def minus(a, b = 0): return (a - b) print(minus(1)) -
We can make an argument optional by using None:
1 2 3 4 5def 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 6def 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 7from 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 3def foo(): """This is a docstring.""" return 'bar' -
Use annotations/type hints to improve the docs: (optional and informational only)
1 2 3def 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 4class Person: def __init__(self, name, age): self.name = name self.age = age -
Default value:
1 2 3class foo: def __init__(self, value): self.value = 0 -
Inheritance:
1 2 3 4 5 6from 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 2from random import randint print(randint(1,6)) -
randrange (end exclusive)
-
choice:
1 2 3from random import choice A = [1,2,3,4,5,6] print(choice(A))
Files
-
Reading an entire file:
1 2 3 4 5 6 7with 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 3with 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 6try: 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 7try: 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 10import 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 pycodestylepycodestyle --show-source --show-pep8 foo.pytests for PEP8 compliance. -
For multiline with binary operators, break before the operators:
1 2 3 4 5income = (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 Noneif any return statement returns an expression.1 2 3 4 5 6 7 8 9 10def 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 7if 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 5if not seq: if seq: # are better than if len(seq): if not len(seq): -
pip3 install autopep8autopep8 --in-place foo.pyauto-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 7cd 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 7def identity(f): return f @identity def foo(): return 'bar' # This same as foo = identity(foo) -
Static methods:
1 2 3 4 5 6class 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 7class 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 11class 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 9def 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__.pyfile for the directory to be considered a submodule.