Python
Fundamentals
Types
Type reflection
class Car:
pass
class Truck(Car):
pass
car = Car()
truck = Truck()
print(type(truck) == type(car))
print(isinstance(car, Car))
print(isinstance(car, Truck))
print(isinstance(truck, Truck))
print(isinstance(truck, Car))
False
True
False
True
True
w = 42
y = ['42']
z = ['42']
print(type(w))
print(type(y))
print(type(z[0]))
print(type(True))
print(type(33.5))
<class 'int'>
<class 'list'>
<class 'str'>
<class 'bool'>
<class 'float'>
Identity
Numbers, floats, strings are immutable
w = 42 x = 42 y = ['42'] z = ['42'] print(id(w)) print(id(x)) print(id(34.3)) print(id(34.3)) print(id(y[0])) print(id(z[0]))
4383542560 4383542560 4385010312 4385010312 4385841648 4385841648
Lists and other container objects are not
w = 42 x = 42 y = ['42'] z = ['42'] print(id(y)) print(id(z))
4563076296 4563259016
Comparison
By value
- Use
==
,!=
,<
,>
,<=
,>=
- Use
By identity
- Use
is
,is not
- Use
Mutable and immutable objects
print('immutable:')
immutable = 42
print(id(immutable))
immutable += 1
print(id(immutable))
immutable -= 1
print(id(immutable))
print('\nimmutable:')
immutable = 'str'
print(id(immutable))
immutable += '1'
print(immutable)
print(id(immutable))
immutable = immutable.replace('1', '')
print(id(immutable))
print('\nmutable:')
mutable = [1, 2, 3]
print(mutable)
print(id(mutable))
mutable.append(4)
print(mutable)
print(id(mutable))
mutable.append(5)
print(mutable)
print(id(mutable))
immutable:
4430617888
4430617920
4430617888
immutable:
4432005024
str1
4433423528
4434276624
mutable:
[1, 2, 3]
4434411656
[1, 2, 3, 4]
4434411656
[1, 2, 3, 4, 5]
4434411656
Built-in functions
len
name = "jebediah"
return name.__len__()
8
name = "jebediah"
return len(name)
8
a_string = "abc"
for char in a_string:
print(char)
a
b
c
range
# 0-indexed range, given n is non-inclusive least upper bound
for n in range(3):
print(n)
0
1
2
# specify start n with two args
for n in range(3, 5):
print(n)
3
4
max
and min
names = ["abe", "Abe"]
return min(names)
'Abe'
names = ['abe', 'Abe']
return max(names)
'abe'
names = ['kat', 'joe', 'ron']
return max(names)
'ron'
pow
return pow(2, 3)
8
return 2**3
8
sorted
names = ['sue', 'jerry', 'linda']
return sorted(names)
['jerry', 'linda', 'sue']
names = ['sue', 'jerry', 'linda']
return sorted(names, reverse=True)
['sue', 'linda', 'jerry']
names = ['sue', 'jerry', ' linda']
return sorted(names)
[' linda', 'jerry', 'sue']
names = ['sue', 'jerry', ' linda']
return sorted(names, key=str.strip)
['jerry', ' linda', 'sue']
names = {'sue': 1, 'jerry': 4, 'linda': 55}
return sorted(names)
['jerry', 'linda', 'sue']
names = [('sue', 100), ('jerry', 4), ('linda', 55)]
return sorted(names, key=lambda s: s[1])
[('jerry', 4), ('linda', 55), ('sue', 100)]
Input/output
Command-line args
Print and remove arguments
import sys sys.argv=['script.py', 135, 33] print(len(sys.argv)) print(sys.argv) sys.argv.remove(sys.argv[0]) print(sys.argv)
3 ['script.py', 135, 33] [135, 33]
Standard Input/Output
try:
color = input("What's your favorite color? ")
except Exception:
color = 'red'
print(color)
What's your favorite color? red
File opening and reading
File I/O: Reading by lines
with open('file.txt', 'r') as f: for line in f: pass # ...
File I/O: Reading by chunks
chunksize = 50_000 with open(filename) as f: while True: chunk = f.read(chunksize) if not chunk: break # if None, file has been exhausted, so break print(line) # (or yield / do whatever)
File seeking
Pointer keeps track of position in file. Closing and re-opening resets the pointer.
filename = 'data.md'
data_file = open(filename, 'w')
data_file.write(
"""
File Seeking
============
This is markdown
----------------
Test one two.
""")
data_file.close()
data_file = open(filename, 'r')
print("reading:")
print(data_file.read(30))
print("--- --- ---")
print(data_file.read(30))
print("--- resetting pointer ---")
print(data_file.seek(0))
print("reading:")
print(data_file.read(30))
data_file.close()
import os
os.remove(filename)
reading:
File Seeking
============
Th
--- --- ---
is is markdown
---------------
--- resetting pointer ---
0
reading:
File Seeking
============
Th
Iteratively read files
filename = './data.txt'
file_data = open(filename, 'w')
file_data.write('one\ntwo\nthree')
file_data.close()
file_data = open(filename, 'r')
print(file_data.readline().rstrip())
print(file_data.readline().rstrip())
print("-- reset --")
file_data.seek(0)
print(file_data.readline().rstrip())
print("-- reset --")
file_data.seek(0)
for line in file_data:
print(line.replace("o", "O").rstrip())
file_data.close()
import os
os.remove(filename)
one
two
-- reset --
one
-- reset --
One
twO
three
Tempfile
import tempfile
tempf = tempfile.TemporaryFile()
# note the 'b' to turn str into a binary literal
tempf.write(b"save this number: 4334433")
tempf.seek(0)
print(tempf.read())
tempf.close()
b'save this number: 4334433'
Manipulating zip files
import zipfile as zf
zipf = zf.ZipFile('Archive.zip', 'r')
print(zipf.namelist())
print(zipf.getinfo('filename.txt'))
# metadata
zipf.infolist()
# access to files in zip dir
print(zipf.read('filename.txt'))
with zipf.open('filename.txt') as f:
print(f.read())
# extracting files
zipf.extract('purchased.txt')
zipf.extractall()
# Closing the zip
zipf.close()
Variadic and keyword arguments
def a_named_func(arg1, arg2=None, *rest, **kwargs):
print(arg1)
print(arg2)
print(rest) # tuple
print(kwargs) # dict
a_named_func(1, 2, 3, 4, 5, six=6, seven=7, eight=8)
a_named_func(1, six=6, eight=8, seven=7)
1
2
(3, 4, 5)
{'six': 6, 'seven': 7, 'eight': 8}
1
None
()
{'six': 6, 'eight': 8, 'seven': 7}
Bitwise operations
def bin(n): return '{:08b}'.format(n)
print('%s (%s) [5]' % (bin(5) , ()))
x, y = 0x55, 0xaa
print('---------')
print("%s (%s) [x]" % (bin(x) , (x)))
print("%s (%s) [y]" % (bin(y) , (y)))
print('- - - - -')
print("%s (%s) [or]" % (bin(x | y) , (x | y)))
print("%s (%s) [and]" % (bin(x & y) , (x & y)))
print("%s (%s) [xor]" % (bin(x ^ y) , (x ^ y)))
print('---------')
print("%s (%s) [x ^ 0]" % (bin(x ^ 0) , (x ^ 0)))
print("%s (%s) [x ^ x]" % (bin(x ^ x) , (x ^ x)))
print('---------')
ten = 10
print("%s (%s) [ten]" % (bin(ten) , (ten)))
print("%s (%s) [ten << 1]" % (bin(ten << 1) , (ten << 1)))
print("%s (%s) [ten << 2]" % (bin(ten << 2) , (ten << 2)))
00000101 (()) [5]
---------
01010101 (85) [x]
10101010 (170) [y]
- - - - -
11111111 (255) [or]
00000000 (0) [and]
11111111 (255) [xor]
---------
01010101 (85) [x ^ 0]
00000000 (0) [x ^ x]
---------
00001010 (10) [ten]
00010100 (20) [ten << 1]
00101000 (40) [ten << 2]
Bytes and Byte arrays
utf8string = 'Ďelta ēpsilon'
print(ord(utf8string[0]))
print(ord('z'))
print(chr(127))
outbytes = bytearray()
for char in utf8string:
if ord(char) < 127:
outbytes.append(ord(char))
else:
outbytes += bytes('&#{:04d};'.format(ord(char)), encoding='utf_8')
outstr = str(outbytes, encoding='utf_8')
print(outstr)
270
122
Ďelta ēpsilon
Regular Expressions
- import
re
- compile a pattern (e.g.
r'[:alpha:]'
)- Pre-compiling allows for efficient re-use
- use the pattern in
re
functions
re.search
import re
letters = re.compile('[a-z]')
numbers = re.compile('[0-9]')
# note the object-oriented style here (re_object.search)
letters_match = letters.search('one two three')
print(letters_match)
print(letters_match[0])
# note the functional style here (re.search)
digits_match = re.search(numbers, 'one two three')
print(digits_match)
<_sre.SRE_Match object; span=(0, 1), match='o'>
o
None
re.sub
import re
string = 'hellosam'
newstr1 = re.sub('[aeiou]', '+', string)
print(newstr1)
vowels_regex = re.compile('(he|am)')
newstr2 = vowels_regex.sub('+', string)
print(newstr2)
h+ll+s+m
+llos+
re.split
import re
elements = re.split('\W+', 'one=two -three+four five', maxsplit=3, flags=re.IGNORECASE)
print(elements)
['one', 'two', 'three', 'four five']
re.match
import re
match1 = re.match('(A)+', 'aaabcabcabc', flags=re.IGNORECASE)
print(match1.group(0))
print(match1.group(1))
match2 = re.match('(F)+', 'aaabcabcabc', flags=re.IGNORECASE)
print(match2)
aaa
a
None
re.fullmatch
import re
matches_full1 = re.fullmatch('a+', 'aaabcabcabc', re.IGNORECASE)
print(matches_full1)
matches_full2 = re.fullmatch('(a+bc)+', 'aaabcabcabc', re.IGNORECASE)
print(matches_full2[0])
print(matches_full2.group(0))
print(matches_full2.group(1))
None
aaabcabcabc
aaabcabcabc
abc
re.findall
import re
matches = re.findall('abc', 'aaaBCaBCaBC', flags=re.IGNORECASE)
print(matches)
['aBC', 'aBC', 'aBC']
re.finditer
import re
matches = re.finditer('abc', 'aaaBCaBCaBC', flags=re.IGNORECASE)
print(matches)
print([match[0] for match in matches])
<callable_iterator object at 0x10926e208>
['aBC', 'aBC', 'aBC']
Collections
Slicing
a_str = "0123456789"
print(a_str[0])
print(a_str[5:])
print(a_str[0:5])
print(a_str[1:5])
0
56789
01234
1234
a_str = "0123456789"
print(a_str[1:-1])
print(a_str[-4:-1])
print(a_str[-4:])
print(a_str[-4])
12345678
678
6789
6
nums = list(range(10))
print(nums)
# [start_index : end_index (non-inclusive) : every nth element]
print(nums[5::2])
nums[:] = range(10, 20)
print(nums)
print(nums[5::2])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 7, 9]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[15, 17, 19]
Lists, tuples, and dictionaries
- Tuples are immutable (and hence faster), lists mutable.
- Dictionaries are unordered, but you can iterate over them by using a dict view (potentially sorted for maximum consistency across Python implementations)
d = {'one': 1, 'two': 2, 'three': 3}
for k in d.keys():
print('%s : %s' % (k, d[k]))
print('-------------')
for k in sorted(d.keys()):
print('%s : %s' % (k, d[k]))
print('-------------')
for k, v in d.items():
print('%s : %s' % (k, v))
one : 1
two : 2
three : 3
-------------
one : 1
three : 3
two : 2
-------------
one : 1
two : 2
three : 3
# defining with kw args
print(dict(one = 1, two = 2, three = 3))
{'one': 1, 'two': 2, 'three': 3}
Dictionaries
Merging / initializing from another
one = dict(one=1, two=2, three=3)
print(one)
print('---------------')
two = dict(**one, four=4, five=5)
print(two)
two = { **one, 'four': 4, 'five': 5 }
print(two)
print('\nmerging and overwriting')
print('---------------')
two_a = dict(three='three', four='four')
three = { **two, **two_a }
print(three)
{'one': 1, 'two': 2, 'three': 3}
---------------
{'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}
{'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}
merging and overwriting
---------------
{'one': 1, 'two': 2, 'three': 'three', 'four': 'four', 'five': 5}
Accessing: Prefer the get
method to avoid exceptions, set default return
value
x = {}
try:
x['three']
except:
print('not there')
print(x.get('three'))
print(x.get('three', '3'))
not there
None
3
Deleting: Use pop
to return the value removed, otherwise del
x = dict(one=1, two=2, three=3, four=4)
del x['one']
print(x)
print(x.pop('two'))
print(x)
{'two': 2, 'three': 3, 'four': 4}
2
{'three': 3, 'four': 4}
Sequences: Tuples and Lists
Tuples are created with the comma operator, NOT with parens.
tup1 = (1)
print(tup1)
print(type(tup1))
tup1 = (1,)
print(tup1)
print(type(tup1))
1
<class 'int'>
(1,)
<class 'tuple'>
Common sequences methods work on tuples as on lists. Tuples are immutable. Lists are mutable.
tup = 1, 2, 3, 4, 5
print(tup)
print(tup[0])
print(tup[-1])
print(len(tup))
print(max(tup))
(1, 2, 3, 4, 5)
1
5
5
5
Built-in methods for sequences:
tup = tuple(range(10))
print(tup)
print(len(tup))
print(tup.count(5))
print(tup.index(5))
print(10 in tup)
print(4 in tup)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
10
1
5
False
True
Strings
Predicate methods
print(''.isalnum())
print('this is a string'.isalnum())
print('--------------')
print('this2isastring'.isalnum())
print('this2isastring'.isalpha())
print('thisisastring'.isalpha())
print('--------------')
print('1234'.isdigit())
print('--------------')
print('1234'.isprintable())
print('1234\n'.isprintable())
False
False
--------------
True
False
True
--------------
True
--------------
True
False
Splitting, joining, formatting
Splitting
print('This is a string of words'.split())
print('This is a string of words'.split('i'))
import re
vowels_and_spaces = re.compile('[aeiou ]+')
print(vowels_and_spaces.split('This is a string of words'))
['This', 'is', 'a', 'string', 'of', 'words']
['Th', 's ', 's a str', 'ng of words']
['Th', 's', 's', 'str', 'ng', 'f', 'w', 'rds']
Joining
words = ['this', 'are', 'some', 'words']
print(' '.join(words))
words = ['this', 'are', 'some', 'words']
print(':'.join(words))
this are some words
this:are:some:words
Formatting
string = ' THIS is a string '
print('|{}|'.format(string.upper()))
print('|{}|'.format(string.lower()))
print('|{}|'.format(string.rstrip()))
print('|{}|'.format(string.lstrip()))
print('|{}|'.format(string.strip()))
| THIS IS A STRING |
| this is a string |
| THIS is a string|
|THIS is a string |
|THIS is a string|
To chomp newlines, pass a newline to rstrip
:
string_with_newline = 'this needs to be chomped\n'
print('<{}>'.format(string_with_newline))
print('<{}>'.format(string_with_newline.rstrip('\n')))
<this needs to be chomped
>
<this needs to be chomped>
To center characters in a string
centered_string = 'memento mori'.center(30, ' ')
print('<{}>'.format(centered_string))
< memento mori >
Format strings: Formatting
Use str.format
to interpolate in idiomatic Python 3.
# (% operator is considered obscelescent)
print('This is a string {} {}'.format(2, 44))
print('\nposition numbering')
print('-------')
print('{0} {1}'.format(2, 44))
print('{1} {0}'.format(2, 44))
print('\nleft-pad with zeros')
print('-------')
print('%02d %d' % (2, 44))
print('{0:02d} {1}'.format(2, 44))
print('\nright-align')
print('-------')
print('|{:>5}|{}|{}|'.format(2, 3, 5))
print('|{:>5}|{:>5}|{:>5}|'.format(2, 3, 5))
print('\nleft-align')
print('-------')
print('|{:<5}|{}|{}|'.format(2, 3, 5))
print('|{:<5}|{:<5}|{:<5}|'.format(2, 3, 5))
print('\ncenter-align')
print('-------')
print('|{:^5}|{}|{}|'.format(2, 3, 5))
print('|{:^5}|{:^5}|{:^5}|'.format(2, 3, 5))
print('\ntruncating and padding')
print('-------')
print('|{:>10.6}|'.format('this is a rather long string'))
print('\nformatting numbers')
print('-------')
print('|{:^7d}|{:7.2f}|{:07.2f}|'.format(2, 3, 5))
This is a string 2 44
position numbering
-------
2 44
44 2
left-pad with zeros
-------
02 44
02 44
right-align
-------
| 2|3|5|
| 2| 3| 5|
left-align
-------
|2 |3|5|
|2 |3 |5 |
center-align
-------
| 2 |3|5|
| 2 | 3 | 5 |
truncating and padding
-------
| this i|
formatting numbers
-------
| 2 | 3.00|0005.00|
Comprehensions
List comprehension
Useful for transforming collections. More Pythonic than equivalent higher-order functions.
collection = [1, 2, 3, 4]
successors = [x + 1 for x in collection]
print(successors)
odds = [x for x in collection if x % 2 is not 0]
print(odds)
[2, 3, 4, 5]
[1, 3]
Dict comprehensions
elements = dict(one=1, two=2, three=3, four=4)
successors = {k: v + 1 for k, v in elements.items()}
print(successors)
odds = {k: v for k, v in elements.items() if v % 2 is not 0}
print(odds)
{'one': 2, 'two': 3, 'three': 4, 'four': 5}
{'one': 1, 'three': 3}
Set comprehensions
elements = [1, 1, 1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9]
uniquified_odds = { n for n in elements if n % 2 is not 0 }
print(uniquified_odds)
{1, 3, 5, 7, 9}
Generator comprehensions
first_evens = (i for i in range(11) if i % 2 is 0)
print(first_evens)
print(first_evens.__next__())
print(first_evens.__next__())
print(first_evens.__next__())
print('---')
for n in first_evens:
print(n)
<generator object <genexpr> at 0x1076d4678>
0
2
4
---
6
8
10
Sequences and iteration
Iteration
Loops: while
, for
, enumerate
while
loopn = 0 while n < 3: n += 1 print(n)
1 2 3
for
-iterationfor n in range(1, 4): print(n)
1 2 3
Enumerated iteration
str = 'this is a string' for char in str: if char == 's': print(char) print('\nenumerated:') for (idx, char) in enumerate(str): if idx < 5: print(char)
s s s enumerated: t h i s
Higher-order functions: map
, filter
, reduce
More declarative than bare loops.
collection = [1, 2, 3, 4]
successors = list(map(lambda x: x + 1, collection))
print(successors)
odds = list(filter(lambda x: x % 2 is not 0, collection))
print(odds)
from functools import reduce
summation = reduce(lambda x, y: x + y, collection)
print(summation)
[2, 3, 4, 5]
[1, 3]
10
Convolution zip
:
Output length determined by length of the shortest iterable.
See itertools.zip_longest
for an alternative version that exhausts all
iterables.
tuples = list(zip(range(5), range(15), reversed(range(10))))
print(tuples)
[(0, 0, 9), (1, 1, 8), (2, 2, 7), (3, 3, 6), (4, 4, 5)]
Higher-order predicates: any
and all
Basic usage
Can be combined with higher-order functions or list comprehensions efficiently (both return iterators instead of eagerly building lists)
nums = list(range(11)) print(any(map(lambda n: n % 2 is 0, nums))) print(any(n % 2 is 0 for n in nums)) print(all(map(lambda n: n % 2 is 0, nums))) print(all(n % 2 is 0 for n in nums))
True True False False
With keyword args for concise filtering
from collections import namedtuple from pprint import pprint Dog = namedtuple('dog', 'name age preferred_pronoun') records = ['rover1,4,he', 'rover2,2,she', 'rover3,3,she', 'rover4,4,he'] dogs = [Dog(*record.split(',')) for record in records] def find_dogs(**kwargs): return [ dog for dog in dogs if all(getattr(dog, key) == value for key, value in kwargs.items()) ] dogs_list = find_dogs(age='4', preferred_pronoun='he') pprint(dogs_list)
[dog(name='rover1', age='4', preferred_pronoun='he'), dog(name='rover4', age='4', preferred_pronoun='he')]
Generator functions: Creating sequences
Use to create iterators
import math
def is_prime(n):
if n < 2:
return False
for i in range(2, math.floor(math.sqrt(n)) + 1):
if i % 2:
return False
return True
def primes(lower_bound=3, upper_bound=50):
while lower_bound <= upper_bound:
if is_prime(lower_bound):
yield lower_bound
lower_bound += 2
print('primes:')
for prime in primes(upper_bound=10, lower_bound=5):
print(prime)
primes:
5
7
Decorators
Use case: Creating accessor properties for instance variables
class DogStack:
def __init__(self, **kwargs):
self._label = kwargs.get('label', 'dogstack1')
self._stack = []
@property
def stack(self):
return self._stack
def push(self, *items):
self.stack.extend(items)
return self
@property
def last(self):
if len(self.stack) is 0:
return None
return self.stack[-1]
@last.setter
def last(self, element):
if len(self.stack) is 0:
self.stack.append(element)
else:
self.stack[-1] = element
@last.deleter
def last(self):
self.stack.pop()
dog = DogStack()
print(dog.last)
dog.push('one', 'two', 'three').push('four')
print(dog.stack)
print(dog.last)
dog.last = 333
print(dog.stack)
del dog.last
print(dog.stack)
None
['one', 'two', 'three', 'four']
four
['one', 'two', 'three', 333]
['one', 'two', 'three']
Use case: arbitrary decoration
Note the use of functools.wraps
to enable fully transparent decoration.
In particular, reflection on the function being decorated will display the
underlying function’s properties, not the decorator’s. Also, the docstring of
the underlying function will be displayed by help
.
See functools.lru_cache
for an extension.
import functools
def printargs(func):
@functools.wraps(func)
def decorated(*args, **kwargs):
message = "Function `{0}' called with args: {1}, kwargs: {2}"
print(message.format(func.__name__, args, kwargs))
return func(*args, **kwargs)
return decorated
@printargs
def fibonacci(n, memo = {0: 1, 1: 1}):
"""Return the nth fibonacci number."""
if n < 0: return None
if n not in memo:
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
return memo[n]
print(fibonacci) # use functools.wraps for transparent decoration
print('---------')
help(fibonacci) # use functools.wraps for transparent decoration
print('---------')
print(fibonacci(8))
<function fibonacci at 0x10f205bf8>
---------
Help on function fibonacci in module __main__:
fibonacci(n, memo={0: 1, 1: 1})
Return the nth fibonacci number.
---------
Function `fibonacci' called with args: (8,), kwargs: {}
Function `fibonacci' called with args: (7, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (6, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (5, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (4, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (3, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (2, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (1, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (0, {0: 1, 1: 1}), kwargs: {}
Function `fibonacci' called with args: (1, {0: 1, 1: 1, 2: 2}), kwargs: {}
Function `fibonacci' called with args: (2, {0: 1, 1: 1, 2: 2, 3: 3}), kwargs: {}
Function `fibonacci' called with args: (3, {0: 1, 1: 1, 2: 2, 3: 3, 4: 5}), kwargs: {}
Function `fibonacci' called with args: (4, {0: 1, 1: 1, 2: 2, 3: 3, 4: 5, 5: 8}), kwargs: {}
Function `fibonacci' called with args: (5, {0: 1, 1: 1, 2: 2, 3: 3, 4: 5, 5: 8, 6: 13}), kwargs: {}
Function `fibonacci' called with args: (6, {0: 1, 1: 1, 2: 2, 3: 3, 4: 5, 5: 8, 6: 13, 7: 21}), kwargs: {}
34
Math
Constants
import math
print(math.pi)
print(math.e)
print(math.nan)
print(math.inf)
print(-math.inf)
3.141592653589793
2.718281828459045
nan
inf
-inf
Trigonometry
import math
# sin: opp / hyp
# cos: adj / hyp
# tan: opp / adj = sin/cos
# (pi / 4): 45 degs
print(math.cos(math.pi / 4))
print(math.sin(math.pi / 4))
print(math.tan(math.pi / 4))
0.7071067811865476
0.7071067811865475
0.9999999999999999
import math
print(math.radians(180) == math.pi)
print(math.radians(360) == 2 * math.pi)
print(math.degrees(2 * math.pi))
True
True
360.0
Floor and ceiling
import math
print(math.ceil(10.10))
print(math.floor(10.10))
print(round(10.10))
print(round(10.50))
print(round(10.51))
11
10
10
10
11
Factorial, sqrt, gcd
import math
print(math.factorial(3))
print(math.sqrt(64))
print(math.gcd(52, 8))
6
8.0
4
Random
randrange
, choice
, sample
, shuffle
import random
print(random.random())
0.6601087654116915
import random
decider = random.randrange(2)
print('heads' if decider == 0 else 'tails')
tails
import random
# rand int 1 through 6
print(random.randrange(1, 7))
5
import random
winners = random.sample(range(1, 101), 5)
print(winners)
[92, 94, 68, 39, 46]
import random
possibilities = ['cat', 'dog', 'fish']
print(random.choice(possibilities))
cat
import random
possibilities = ['cat', 'dog', 'fish']
random.shuffle(possibilities)
print(possibilities)
['dog', 'cat', 'fish']
randrange
, randint
import random as rand
# shuffle
rands = list(range(15))
rand.shuffle(rands)
print(rands)
# randrange:
print([rand.randrange(1, 10 + 1) for x in range(15)])
# alias for the above:
print([rand.randint(1, 10) for x in range(15)])
[10, 13, 2, 1, 4, 0, 9, 14, 12, 5, 7, 3, 6, 8, 11]
[10, 2, 6, 6, 8, 1, 5, 4, 7, 9, 7, 7, 6, 9, 5]
[1, 4, 3, 10, 3, 9, 5, 6, 5, 2, 7, 9, 7, 4, 9]
Statistics
Measures of central tendency: Mean, median, mode
import random
import statistics as stats
ages_data = [random.randrange(100) for x in list(range(10))]
print(ages_data)
print(stats.mean(ages_data))
print(stats.median(ages_data))
# when no mode, exception is thrown:
# statistics.StatisticsError: no unique mode; found 3 equally common values
try:
print(stats.mode(ages_data))
except stats.StatisticsError:
print('no mode')
[91, 51, 17, 9, 39, 25, 70, 68, 92, 15]
47.7
45.0
no mode
Measures of spread: variance, stdev
import statistics as stats
ages_data = [10, 13, 14, 12, 11, 10, 11, 10, 15]
print(stats.variance(ages_data))
print(stats.stdev(ages_data))
3.4444444444444446
1.855921454276674
More Collections
defaultdict
Use case: counting
from collections import defaultdict
strs = ['one', 'one', 'two', 'three']
counts = defaultdict(int)
for str in strs:
counts[str] += 1
print(counts)
print(counts['one'])
print(counts.get('two'))
print(dict(counts))
defaultdict(<class 'int'>, {'one': 2, 'two': 1, 'three': 1})
2
1
{'one': 2, 'two': 1, 'three': 1}
Use case: Nested counting
from collections import defaultdict
strs = ['one two', 'one two', 'two one', 'three four']
counts = defaultdict(lambda: defaultdict(int))
for str in strs:
lead, trail = str.split()
counts[lead][trail] += 1
print(counts['one']['two'])
print(counts['two']['one'])
print(counts['three']['four'])
print({ k: dict(v) for k, v in counts.items() })
2
1
1
{'one': {'two': 2}, 'two': {'one': 1}, 'three': {'four': 1}}
namedtuple
from collections import namedtuple
records = [
'1896\tThomas Burke\tUSA\t100m men\n',
'1896\tThomas Burke\tRUS\t100m men\n',
'1896\tThomas Burke\tUSA\t100m men\n',
'1896\tThomas Burke\tUSA\t100m men\n',
'1896\tThomas Burke\tFRA\t100m men\n',
'1896\tThomas Burke\tRUS\t100m men\n',
'1896\tThomas Burke\tCAD\t100m men\n',
'1896\tThomas Burke\tFRA\t100m men\n',
'1896\tThomas Burke\tCAD\t100m men\n',
'1896\tThomas Burke\tFRA\t100m men\n',
'1896\tThomas Burke\tFRA\t100m men\n'
]
Medal = namedtuple('medal', 'year athlete country event')
medal1 = Medal(*records[0].rstrip().split('\t'))
print()
print(medal1)
print(medal1.year)
print(medal1.athlete)
Python 3.6.1 |Continuum Analytics, Inc.| (default, May 11 2017, 13:04:09)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> ... ... ... ... ... ... ... ... ... ... ... ... >>> >>> >>> >>> >>>
medal(year='1896', athlete='Thomas Burke', country='USA', event='100m men')
1896
Thomas Burke
Counter
An alternative to using defaultdict~s. Provides conveniences like ~most_common
.
from collections import Counter
medals = [Medal(*record.rstrip().split('\t')) for record in records]
teams = Counter(medal.country for medal in medals)
print()
print(teams.most_common(3))
print(teams.get('USA'))
print(teams.get('RUS'))
>>> >>> >>> >>>
[('FRA', 4), ('USA', 3), ('RUS', 2)]
3
2
Iteration tools: itertools
Infinite counting: count
import itertools as it
# runs infinitely
for x in it.count(start=10, step=5):
print(x)
if x == 30:
break
10
15
20
25
30
Infinite cycling: cycle
import itertools as it
x = 0
for c in it.cycle("RACECAR"):
print(c)
x += 1
if x > 10:
break
R
A
C
E
C
A
R
R
A
C
E
Infinite repeating: repeat
import itertools as it
x = 0
for r in it.repeat(True):
print(r)
x += 1
if x > 4:
break
True
True
True
True
True
Permutations: permutations
import itertools as it
election = {1: 'Barb', 2: 'Karen', 3: 'Erin'}
for p in it.permutations(election):
print(p)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
import itertools as it
election = {1: 'Barb', 2: 'Karen', 3: 'Erin'}
for p in it.permutations(election.values()):
print(p)
('Barb', 'Karen', 'Erin')
('Barb', 'Erin', 'Karen')
('Karen', 'Barb', 'Erin')
('Karen', 'Erin', 'Barb')
('Erin', 'Barb', 'Karen')
('Erin', 'Karen', 'Barb')
Combinations: combinations
import itertools as it
election = {1: 'Barb', 2: 'Karen', 3: 'Erin'}
for p in it.combinations(election, r=2):
print(p)
(1, 2)
(1, 3)
(2, 3)
import itertools as it
election = {1: 'Barb', 2: 'Karen', 3: 'Erin'}
for p in it.combinations(election.values(), r=2):
print(p)
('Barb', 'Karen')
('Barb', 'Erin')
('Karen', 'Erin')
starmap
: map
a function over a list of arg lists
from itertools import starmap
sums = starmap(lambda x, y: x + y, [(1, 2), (3, 5), (8, 8)])
sums1 = map(lambda x: x[0] + x[1], [(1, 2), (3, 5), (8, 8)])
print(list(sums))
print(list(sums1))
[3, 8, 16]
[3, 8, 16]
Convolution (exhaustive): zip_longest
from itertools import zip_longest
tuples = zip_longest(range(2), range(3), range(5))
print(list(tuples))
tuples = zip_longest(range(2), range(3), range(5), fillvalue=0)
print(list(tuples))
[(0, 0, 0), (1, 1, 1), (None, 2, 2), (None, None, 3), (None, None, 4)]
[(0, 0, 0), (1, 1, 1), (0, 2, 2), (0, 0, 3), (0, 0, 4)]
Functional tools: functools
functools.wraps
See Use case: arbitrary decoration
functools.lru_cache
Provides memoization via a decorator.
from functools import lru_cache
from functools import wraps
def printargs(func):
@wraps(func)
def decorated(*args, **kwargs):
message = "Function `{0}' called with args: {1}, kwargs: {2}"
print(message.format(func.__name__, args, kwargs))
return func(*args, **kwargs)
return decorated
@lru_cache(maxsize=None)
@printargs
def fibonacci(n):
"""Return the nth fibonacci number."""
if n < 0: return None
if n < 2: return 1
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(8))
print(fibonacci.cache_info())
Function `fibonacci' called with args: (8,), kwargs: {}
Function `fibonacci' called with args: (7,), kwargs: {}
Function `fibonacci' called with args: (6,), kwargs: {}
Function `fibonacci' called with args: (5,), kwargs: {}
Function `fibonacci' called with args: (4,), kwargs: {}
Function `fibonacci' called with args: (3,), kwargs: {}
Function `fibonacci' called with args: (2,), kwargs: {}
Function `fibonacci' called with args: (1,), kwargs: {}
Function `fibonacci' called with args: (0,), kwargs: {}
34
CacheInfo(hits=6, misses=9, maxsize=None, currsize=9)
functools.partial
Partial application.
from functools import partial
def nums_list(lower, upper, predicate=lambda x: True):
return [n for n in range(lower, upper + 1) if predicate(n)]
print(nums_list(1, 10))
even_nums_list = partial(nums_list, predicate=lambda x: x % 2 is 0)
print(even_nums_list(1, 10))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 10]
Date/Time, Calendars, Timers
Current time, formatting dates and times
from datetime import datetime
now = datetime.now()
print(now.date())
print(now.time())
print('%02d:%02d:%02d' % (now.hour, now.minute, now.second))
print(now.strftime('%A, %b %d, %Y at %I:%M:%S %p'))
print(now.strftime('%D'))
2017-09-20
11:44:27.997500
11:44:27
Wednesday, Sep 20, 2017 at 11:44:27 AM
09/20/17
import datetime as dt
now = dt.datetime.now()
print(now)
print(now.year)
print(now.hour)
2017-09-20 11:44:28.249418
2017
11
Calculating future times
from datetime import datetime, timedelta
now = datetime.now()
print(now.date())
two_days_from_now = now + timedelta(days=2)
print(two_days_from_now.date())
three_weeks_ago = now - timedelta(weeks=3)
print(three_weeks_ago.date())
if two_days_from_now > now:
two_days_ago = now - (two_days_from_now - now)
print(two_days_ago.date())
2017-09-20
2017-09-22
2017-08-30
2017-09-18
Calendars
import calendar
cal = calendar.month(2001, 10)
print(cal)
October 2001
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
import calendar
cal = calendar.weekday(2001, 10, 11)
print(cal) # returns the zero-indexed day of week
3
import calendar
print(calendar.isleap(2004))
print(calendar.isleap(1996))
print(calendar.isleap(1995))
True
True
False
Creating timers
import time
try:
run = input('Start? > ')
except:
run = 'yes'
seconds = 0
if run == 'yes':
while seconds < 5:
print('> ', seconds)
time.sleep(1)
seconds += 1
Start? > > 0
> 1
> 2
> 3
> 4
Text munging
HTML Parsing: html.parser
from html.parser import HTMLParser
class HTMLParser(HTMLParser):
def handle_starttag(self, tag, attrs):
print('Start tag: ', tag)
for attr in attrs:
print('attr: ', attr)
def handle_endtag(self, tag):
print('End tag: ', tag)
def handle_comment(self, data):
print('Comment: ', data)
def handle_data(self, data):
data = data.rstrip()
if len(data) > 0:
print('Data: ', data)
parser = HTMLParser()
parser.feed("""
<html>
<head>
<title>A web page</title>
</head>
<body>
<!-- this is a comment -->
<h1>this is a header</h1>
</body>
</html>
""")
print()
Start tag: html
Start tag: head
Start tag: title
Data: A web page
End tag: title
End tag: head
Start tag: body
Comment: this is a comment
Start tag: h1
Data: this is a header
End tag: h1
End tag: body
End tag: html
Parsing html from a saved file:
html_file = open('sample-html.html', 'r')
html_str = ""
for line in html_file:
s += line
parser.feed(s)
Text wrapping: textwrap
import textwrap as tw
hymn = """
From the halls of Montezuma to the shores of Tripoli, we fight our country's battles, in the air on land and sea. First to fight for right and freedom, and to keep our honor clean, we our proud to claim the title of United States Marine.
"""
# no dedent, just wrap (default width is 70)
print('Without dedent')
print('--------------')
print(tw.fill(hymn))
# with dedent
print('\nDedent')
print('------')
print(tw.dedent(hymn).strip())
# with fill and width arg (includes dedent)
print('\nFill to width')
print('---------------')
print(tw.fill(hymn, width=60).strip())
print('\nControlling indent')
print('------------------')
print(tw.fill(hymn, initial_indent=' ', subsequent_indent=' '))
print('\nShortening text')
print('----------------')
print(tw.shorten(hymn, width=50, placeholder='...'))
Without dedent
--------------
From the halls of Montezuma to the shores of Tripoli, we fight
our country's battles, in the air on land and sea. First to fight for
right and freedom, and to keep our honor clean, we our proud to claim
the title of United States Marine.
Dedent
------
From the halls of Montezuma to the shores of Tripoli, we fight our country's battles, in the air on land and sea. First to fight for right and freedom, and to keep our honor clean, we our proud to claim the title of United States Marine.
Fill to width
---------------
From the halls of Montezuma to the shores of
Tripoli, we fight our country's battles, in the air on land
and sea. First to fight for right and freedom, and to keep
our honor clean, we our proud to claim the title of United
States Marine.
Controlling indent
------------------
From the halls of Montezuma to the shores of Tripoli, we
fight our country's battles, in the air on land and sea. First to
fight for right and freedom, and to keep our honor clean, we our
proud to claim the title of United States Marine.
Shortening text
----------------
From the halls of Montezuma to the shores of...
HTTP: urllib.request
, json
HTTP, urllib, JSON
import urllib.request as req
import json
import textwrap as tw
from pprint import PrettyPrinter
pp = PrettyPrinter(depth=3)
url = 'https://www.googleapis.com/books/v1/volumes?q=isbn:1101904224'
with req.urlopen(url) as f:
json_string = f.read().decode('utf-8')
dict = json.loads(json_string)
pp.pprint(dict)
{'items': [{'accessInfo': {...},
'etag': '/FXgwbeW4Mk',
'id': '1imJDAAAQBAJ',
'kind': 'books#volume',
'saleInfo': {...},
'searchInfo': {...},
'selfLink': 'https://www.googleapis.com/books/v1/volumes/1imJDAAAQBAJ',
'volumeInfo': {...}}],
'kind': 'books#volumes',
'totalItems': 1}
Batteries-included web-scraping: requests
and bs4
import requests
URL = 'https://jsonplaceholder.typicode.com/posts/1'
resp = requests.get(URL)
print(resp.status_code)
print(resp.text)
print(resp.json())
200
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
{'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}
from bs4 import BeautifulSoup
import requests
URL = 'https://jsonplaceholder.typicode.com'
resp = requests.get(URL)
print('------ status code ------')
print(resp.status_code)
html = BeautifulSoup(resp.text, 'html.parser')
print('------ title ------')
print(html.title)
print(html.title.name)
print(html.title.string)
print('------ p ------')
print(html.p)
print('------ all <a> ------')
print(html.find_all('a'))
print('------ all text ------')
print(html.get_text())
------ status code ------
200
------ title ------
<title>JSONPlaceholder - Fake online REST API for developers</title>
title
JSONPlaceholder - Fake online REST API for developers
------ p ------
<p>
Fake Online REST API for Testing and Prototyping<br/>
<small>
powered by <a href="https://github.com/typicode/json-server">JSON Server</a>
and <a href="https://github.com/typicode/lowdb">lowdb</a>
</small>
</p>
------ all <a> ------
[<a href="https://github.com/typicode/json-server">JSON Server</a>, <a href="https://github.com/typicode/lowdb">lowdb</a>, <a class="twitter-follow-button" data-show-count="false" href="https://twitter.com/typicode">
Follow @typicode
</a>, <a href="/posts">/posts</a>, <a href="/comments">/comments</a>, <a href="/albums">/albums</a>, <a href="/photos">/photos</a>, <a href="/todos">/todos</a>, <a href="/users">/users</a>, <a href="https://github.com/typicode/jsonplaceholder#how-to">examples</a>, <a href="/posts">/posts</a>, <a href="/posts/1">/posts/1</a>, <a href="/posts/1/comments">/posts/1/comments</a>, <a href="/comments?postId=1">/comments?postId=1</a>, <a href="/posts?userId=1">/posts?userId=1</a>, <a href="https://github.com/typicode/json-server">JSON Server</a>, <a href="https://my-json-server.typicode.com">My JSON Server</a>, <a href="https://github.com/typicode">typicode</a>, <a href="https://github.com/typicode/jsonplaceholder">GitHub</a>, <a href="https://patreon.com/typicode" onclick="trackOutboundLink('https://patreon.com/typicode')">Patreon page</a>, <a class="more" href="https://github.com/typicode/hotel">
<p>
Hotel - 🏩 the friendly process manager for developers
</p>
</a>]
------ all text ------
JSONPlaceholder - Fake online REST API for developers
hljs.initHighlightingOnLoad();
JSONPlaceholder
Fake Online REST API for Testing and Prototyping
powered by JSON Server
and lowdb
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
Follow @typicode
Intro
JSONPlaceholder is a free online REST service that you can use whenever you need some fake data.
It's great for tutorials, faking a server, sharing code examples, ...
Example
Run this code in a console or from anywhere (CORS and JSONP supported).
var root = 'http://jsonplaceholder.typicode.com';
$.ajax({
url: root + '/posts/1',
method: 'GET'
}).then(function(data) {
console.log(data);
});
Run
"Try me!"
Resources
Inspired by common use cases.
/posts100 items
/comments500 items
/albums100 items
/photos5000 items
/todos200 items
/users10 items
Routes
All HTTP verbs are supported.
View usage examples.
GET/posts
GET/posts/1
GET/posts/1/comments
GET/comments?postId=1
GET/posts?userId=1
POST/posts
PUT/posts/1
PATCH/posts/1
DELETE/posts/1
Use your OWN data
JSON Server powers this website.
You can use it to create the same fake API in less than 30 seconds with your own data.
npm install -g json-server
Or you can try My JSON Server free service.
Coded and built with
by typicode.
Source code available on
GitHub.
Patreon page.
Hotel - 🏩 the friendly process manager for developers
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-44497010-1', 'typicode.com');
ga('send', 'pageview');
var trackOutboundLink = function(url) {
ga('send', 'event', 'outbound', 'click', url, {
'transport': 'beacon'
});
}
window.twttr = (function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0],
t = window.twttr || {};
if (d.getElementById(id)) return t;
js = d.createElement(s);
js.id = id;
js.src = "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
t._e = [];
t.ready = function(f) {
t._e.push(f);
};
return t;
}(document, "script", "twitter-wjs"));
// Use http or https based on location.protocol
var exampleText = $('#example').text()
$('#example').text(exampleText.replace('http:', location.protocol))
// Highlight result element
$('#result').each(function(i, block) {
hljs.highlightBlock(block);
});
// Run example
$('#run').click(function() {
var root = location.protocol + '//jsonplaceholder.typicode.com';
$.ajax({
url: root + '/posts/1',
method: 'GET'
}).then(function(data) {
var str = JSON.stringify(data, null, '\t')
$('#result').html(
str.replace(/\n/g, '<br/>')
.replace(/\\n/g, ' ')
.replace(/\t/g, ' ')
);
$('#result').each(function(i, block) {
hljs.highlightBlock(block);
});
});
});
// Tell that jQuery can be used in console
console.log('You can use jQuery functions')
Databases: sqlite3
import sqlite3
# create db file, connect to db
dbfile = 'test.db'
db = sqlite3.connect(dbfile)
# Optional: set row factory. Default behavior is to return tuples.
# Using a row factory makes it easy to convert the row object to a dictionary
# by passing a row to the dict initializer (see cursor logic below)
db.row_factory = sqlite3.Row
# SQL statements
db.execute('drop table if exists messages;')
db.execute('create table messages (id int, body text);')
insert_statement = 'insert into messages (id, body) values (?, ?);'
db.execute(insert_statement, (1, 'first!'))
db.execute(insert_statement, (2, 'this is a second message'))
db.commit()
cursor = db.execute('select * from messages order by id;')
for row in cursor:
row_dict = dict(row)
print(row_dict)
import os
os.remove(dbfile)
{'id': 1, 'body': 'first!'}
{'id': 2, 'body': 'this is a second message'}
Testing: unittest
class Speaker():
def sayHi(self):
return 'hi'
# import speaker
import unittest
class TestSpeaker(unittest.TestCase):
def setUp(self):
return None
def testSayHi(self):
speaker = Speaker()
greeting = speaker.sayHi()
self.assertEqual(greeting, 'hi')
# the following is just for displaying results here:
from unittest import TextTestRunner, TestLoader
from io import StringIO
output = StringIO()
test_suite = TestLoader().loadTestsFromTestCase(TestSpeaker)
test_result = TextTestRunner(output).run(test_suite)
print(output.getvalue())
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Web development: flask
Packages and utilities
- Flask
- werkzeug (WSGI utilities)
- jinja2 (template engine)
- Bootswatch (bootstrap themes)
- psycopg2:
pip install psycopg2
- Flask extensions:
- SQLAlchemy:
pip install flask-sqlalchemy
- Flask-WTF:
pip install flask-wtf
- SQLAlchemy:
Routes / Controller
routes.py
:
from flask import Flask, redirect, render_template, session, url_for
from forms import LoginForm, SignupForm
from models import User, db
app = Flask(__name__)
app.config['SQLALCHEMY_DB_URI'] = 'postgresql://localhost/dbnamehere'
app.secret_key = 'dev-key'
db.init_app(app)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if 'email' in session:
return redirect(url_for('home'))
form = SignupForm()
if request.method == 'GET':
return render_template('signup.html', form=form)
elif request.method == 'POST':
if form.validate():
newuser = User(form.first_name.data, form.last_name.data,
form.email.data, form.password.data)
db.session.add(newuser)
db.session.commit()
# create cookie for current session
session['email'] = newuser.email
return redirect(url_for('home'))
else:
return render_template('signup.html', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
if 'email' in session:
return redirect(url_for('home'))
form = LoginForm()
if request.method == 'GET':
return render_template('login.html')
if request.method == 'POST':
if not form.validate():
return render_template('login.html', form=form)
user = User.query.filter_by(email=form.email.data).first()
if user is None or user.check_password(form.password.data):
return render_template('login.html', form=form)
session['email'] = user.email
return redirect(url_for('home'))
@app.route('/logout', methods=['GET'])
def logout():
session.pop('email', None)
return redirect(url_for('index'))
@app.route('/home', methods=['GET'])
def home():
if 'email' in session:
return render_template('home.html')
else:
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
Models
models.py
:
from flask.ext.sqlalchemy import SQLAlchemy
from werkzeug import check_password_hash, generate_password_hash
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
uid = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(100))
lastname = db.Column(db.String(100))
email = db.Column(db.String(120), unique=True)
pwdhash = db.Column(db.String(54))
def __init__(self, firstname, lastname, email, password):
self.firstname = firstname
self.lastname = lastname
self.email = email
self.set_password(password)
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
Forms
from flask_wtf import Form
from wtforms import PasswordField, StringField, SubmitField
from wtforms.validators import DataRequired, Email, Length
class SignupFrom(Form):
first_name = StringField(
'First name',
validators=[DataRequired('Please enter your first name.')])
last_name = StringField(
'Last name', validators=[DataRequired('Please enter your last name.')])
email = StringField(
'Email',
validators=[
DataRequired('Please enter a valid email address.'),
Email('Please enter a valid email address.')
])
password = PasswordField(
'Password',
validators=[
DataRequired('Please enter a password.'),
Length(
min=6, message='Password must be at least 6 characters long.')
])
submit = SubmitField('Sign up')
class LoginForm(Form):
email = StringField(
'Email',
validators=[
DataRequired('Please enter a valid email address.'),
Email('Please enter a valid email address.')
])
password = PasswordField(
'Password', validators=[DataRequired('Please enter a password.')])
submit = SubmitField('Log in')
Geocoding and geoplotting: geopy
and matplotlib
Geocoding
import geopy
from pprint import pprint
olympics_data = """
Athens (1896)
Paris (1900)
St Louis (1904)
London (1908)
Stockholm (1912)
Antwerp (1920)
Paris (1924)
Amsterdam (1928)
Los Angeles (1932)
Berlin (1936)
London (1948)
Helsinki (1952)
Melbourne / Stockholm (1956)
Rome (1960)
Tokyo (1964)
Mexico (1968)
"""
def location(entry):
*city, year = entry.split()
city = ' '.join(city)
year = year.strip('()')
return (city.split('/')[0], city, year)
geolocator = geopy.geocoders.Nominatim()
cities = {}
for entry in olympics_data.strip().split('\n'):
city_name, full_city, _year = location(entry)
cities[full_city] = geolocator.geocode(city_name)
pprint(cities)
{'Amsterdam': Location(Amsterdam, Centrum, Amsterdam, MRA, Stadsregio Amsterdam, Noord-Holland, Nederland, (52.3745403, 4.89797550561798, 0.0)),
'Antwerp': Location(Antwerpen, Vlaanderen, België - Belgique - Belgien, (51.2211097, 4.3997081, 0.0)),
'Athens': Location(Athens, Athens - Clarke County, Georgia, United States of America, (33.9550905, -83.3881868, 0.0)),
'Berlin': Location(Berlin, Deutschland, (52.5170365, 13.3888599, 0.0)),
'Helsinki': Location(Helsinki, Helsingin seutukunta, Uusimaa, Etelä-Suomi, Suomi, (60.1674086, 24.9425683, 0.0)),
'London': Location(London, Greater London, England, UK, (51.5073219, -0.1276474, 0.0)),
'Los Angeles': Location(LA, Los Angeles County, California, United States of America, (34.054935, -118.244476, 0.0)),
'Melbourne / Stockholm': Location(Melbourne, City of Melbourne, Greater Melbourne, Victoria, 3000, Australia, (-37.8142176, 144.9631608, 0.0)),
'Mexico': Location(Ciudad de México, Cuauhtémoc, México, (19.4326009, -99.1333416, 0.0)),
'Paris': Location(Paris, Île-de-France, France métropolitaine, 75000;75001;75002;75003;75004;75005;75006;75007;75008;75009;75010;75011;75012;75013;75014;75015;75016;75017;75018;75019;75020;75116, France, (48.8566101, 2.3514992, 0.0)),
'Rome': Location(Roma, RM, LAZ, Italia, (41.8933203, 12.4829321, 0.0)),
'St Louis': Location(St. Louis, City of St. Louis, Missouri, United States of America, (38.6272733, -90.1978889, 0.0)),
'Stockholm': Location(Sthlm, Stockholm, Landskapet Uppland, Stockholms län, Svealand, Sverige, (59.3251172, 18.0710935, 0.0)),
'Tokyo': Location(東京都, 日本, (34.2255804, 139.294774527387, 0.0))}
Plotting
import matplotlib.pyplot as pp
from mpl_toolkits.basemap import Basemap
pp.figure(figsize=(10, 5))
world = Basemap()
world.drawcoastlines(linewidth=0.25)
world.drawcountries(linewidth=0.25)
for city, posn in cities.items():
world.plot(posn.longitude, posn.latitude, 'r.', markersize=10, latlon=True)
More libraries
- Pillow: manipulate images
- matplotlib: make plots
Classes
Enums
from enum import Enum
class Fruit(Enum):
APPLE = 1
BANANA = 2
MANGO = 3
PINEAPPLE = 4
GUAVA = 5
print(Fruit.APPLE)
print(Fruit.APPLE == Fruit.APPLE)
print(Fruit.APPLE == 1)
class Fruit:
APPLE = 1
BANANA = 2
MANGO = 3
PINEAPPLE = 4
GUAVA = 5
print(Fruit.APPLE)
print(Fruit.APPLE == Fruit.APPLE)
print(Fruit.APPLE == 1)
Can enforce uniquness
from enum import Enum
class Fruit(Enum):
APPLE = 1
ORANGE = 1
print(Fruit.APPLE)
print(Fruit.ORANGE)
from enum import Enum, unique
try:
@unique
class Fruit(Enum):
APPLE = 1
ORANGE = 1
except ValueError as e:
print(e)
Can assign values automatically
from enum import Enum, auto
class Fruit(Enum):
APPLE = auto()
ORANGE = auto()
print(Fruit.APPLE.value)
print(Fruit.ORANGE.value)
Class string functions
str
, format
, repr
, bytes
String function | Called when x is called |
---|---|
obj.__str__(self) | str(obj) , print(obj) , "{obj}".format(obj) |
obj.__repr__(self) | repr(obj) |
obj.format(self, format_spec) | format(obj, format_spec) |
obj.__bytes__(self) | bytes(obj) |
class Person:
def __init__(self, fname, lname, age):
self.fname = fname
self.lname = lname
self.age = age
def __repr__(self):
return f'Person({self.fname}, {self.lname}, {self.age})'
def __str__(self):
return f'<Person: {self.fname} {self.lname}>'
person = Person('Joe', 'Marini', 25)
# with neither __repr__ or __str__ defined:
# <__main__.Person object at 0x1013e16a0>
print(repr(person)) # Rule of thumb: ~repr~ should be copy-pastable to create a new record
print(str(person)) # `print` calls `str`, which falls back to `repr` if undefined
Computed attributes
from datetime import datetime as dt
class Dog:
def __init__(self, vaccination_year=None):
self.vaccination_date = dt.strptime(str(vaccination_year), '%Y') if vaccination_year else None
@property
def has_been_vaccinated(self):
return self.vaccination_date and self.vaccination_date <= dt.now()
dog = Dog(vaccination_year=2018)
print(dog.has_been_vaccinated)
Custom class numerical operators
class Molecule:
def __init__(self, *elements):
self.elements = list(elements)
def __add__(self, other):
for e in other.elements:
self.elements.append(e)
return self
def __str__(self):
return str(self.elements)
class Atom:
def __init__(self, symbol):
self.symbol = symbol
self.elements = [self]
def __add__(self, other):
molecule = Molecule()
for e in other.elements:
molecule = molecule + e
return molecule
def __mul__(self, factor):
molecule = Molecule()
for _ in range(factor):
molecule = molecule + self
return molecule
def __repr__(self):
return self.symbol
h2 = Atom('H') * 2
print(h2)
o = Atom('O')
print(o)
water = h2 + o
print(water)
Logging
Logging levels
Message levels | Logging API | Description |
---|---|---|
DEBUG | logging.debug() | Diagnostic |
INFO | logging.info() | General |
WARNING | logging.warning() | Something unexpected, or an approaching problem |
ERROR | logging.error() | Unable to perform a specific operation |
CRITICAL | logging.critical() | Program may not be able to continue |
Initializing
import logging
fmtstr = "%(asctime)s: %(levelname)s: %(funcName)s Line:%(lineno)d %(message)s"
datestr = "%m/%d/%Y %I:%M %M"
logging.basicConfig(
level=logging.DEBUG,
filename='output.log',
filemode='w',
format=fmtstr,
datefmt=datestr)
logging.error("This is an info-level log message")
Generators
Refresher
Generators maintain state and provide lazy sequences / sequential evaluation of functions. Generator functions return generator objects (an iterator).
Generator expression / generator comprehension
nums = [7, 22, 4.5, 99.7, '6', '5']
ints = (int(i) for i in nums)
print([i for i in ints if i % 2 is 0])
nums = [7, 22, 4.5, 99.7, '6', '5']
evens = (int(i) for i in nums if int(i) % 2 is 0)
print(evens.__next__())
print(evens.__next__())
print(evens.__next__())
try:
print(evens.__next__())
except StopIteration as err:
print("StopIteration", err)
Generator function
def even_integers(lower, upper):
for i in range(lower, upper + 1):
if i % 2 is 0:
yield i
evens = even_integers(3, 23)
print(evens.__next__())
print(evens.__next__())
print(evens.__next__())
Generator pipelines
def open(filename):
return """
Tammie Sugrue
Roselyn Burr
Marybeth Candelario
Mathilda Wimer
Berneice Dooley
Ana Chou
Valencia Harrold
Shelby Lao
Felton Healey
Tammy Locklear
Cedrick Kehoe
Willene Clingman
Norene Snedden
Alaine Magruder
Sid Letellier
Corrie Cutlip
Staci Carlow
Elton Rakes
Gwendolyn Mannion
Emilie Primmer
"""
full_names = (name.strip() for name in open("names.txt").strip().split("\n"))
lengths = ((name, len(name)) for name in full_names)
longest = max(lengths, key=lambda x: x[1])
print(longest)
Context managers: with
Python object that’s able to act as a control structure after the with
statement. Typically used to handle setup and teardown logic.
The basic form:
from contextlib import contextmanager
@contextmanager
def simple_cm(*args):
try:
# setup code
yield
finally:
# teardown code
Coroutines
Properties
- Built from a generator, but conceptually different
- Repeatedly receives input
- Processes input
- Stops at a yield statement
- Unlike a function, has persistent properties that can be changed
yield
not only pauses flow, also captures sent values
Minimal Example
def coroutine():
while True:
x = yield
print(x)
try:
cor = coroutine()
cor.send(48)
except TypeError as err:
print("can't send non-None value to a just started generator")
cor.__next__() # start generator
cor.send(48)
cor.send(55)
Threading
Minimal Example: multiprocessing.dummy
multiprocessing.dummy
replicates the API of multiprocessing
but is just a
wrapper around the threading
module.
Multi-threaded
Runtime: 0.5225861072540283
import requests
from time import time
from multiprocessing.dummy import Pool as ThreadPool
from pprint import pprint
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
start_time = time()
thread_pool = ThreadPool(len(urls))
results = thread_pool.map(requests.get, urls)
thread_pool.close()
thread_pool.join()
pprint(results)
pprint("runtime: {}".format(time() - start_time))
Comparison to single-threaded implementation
Runtime: 1.4841949939727783
import requests
from time import time
from pprint import pprint
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
start_time = time()
results = map(requests.get, urls)
pprint(list(results))
pprint("runtime: {}".format(time() - start_time))
Multiprocessing
Minimal Example: Pool
(preferred)
import requests
from time import time
from multiprocessing import Pool
from pprint import pprint
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
start_time = time()
with Pool(len(urls)) as pool:
results = pool.map(requests.get, urls)
pprint(results)
pprint("runtime: {}".format(time() - start_time))
Minimal Example: Process
import requests
from time import time
from multiprocessing import Process
from pprint import pprint
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'https://wiki.python.org/moin/',
]
start_time = time()
get_requests = [Process(target=requests.get, args=(url,)) for url in urls]
for get_request in get_requests:
get_request.start()
get_request.join()
pprint(get_requests)
pprint("runtime: {}".format(time() - start_time))