Note: Most of the example codes are adapted from the excellent 'Learning Python' by Fabrizio Romano
Refer to PEP8 - https://www.python.org/dev/peps/pep-0008/
package, module
module
- a .py file - a python module
package
- group set of modules together
- a folder which has .py files
- it must contain a
__init__.py
file inside this folder for it to qualify as a package - as of 3.3 ,
__init__.py
is not strictly required
Class
class Bike:
def __init__(self, color, frame_material):
self.color = color
self.frame_material = frame_material
def print_detail(self):
print("color : %s, frame_material: %s" % (self.color, self.frame_material))
red_bike = Bike('Red', 'Carbon fiber')
red_bike.print_detail() # color : Red, frame_material: Carbon fiber
DataClass
from dataclasses import dataclass
@dataclass
class Employee:
""" Immutable public data of employee information """
first_name: str
middle_name: str
last_name: str
age: str
department_id: int
employee1 = Employee("John", "", "Doe", 23, 1)
employee2 = Employee("Jane", "", "Doe", 20, 2)
Order of scope evaluation while trying to find a name in a namespace
LEGB (Local, Enclosing function, Global, Built-in)
If you have a function and want to modify a global variable within the function (Not recommended but if needed)
x = 10
def func():
global x # 'x' is in global namespace
x += 1
func()
# x is now 11
If you have a nested function and want to modify a variable in the outer scope (Not recommended but if needed)
def outer():
x = 10
def inner():
nonlocal x # modifies outer 'x'
x += 1
inner()
# x is now 11
from module_name import identifier
from module_name import identifier as better_identifier
import module_name
from .module_name import identifier # number of dots specify the number of folders to backtrack up and one 'dot' is current folder
Almost everything is an object in python
a = 10 # a is pointing to an object containing 10 which is of int data type
a ** b # ** is a power operator
a // b # integer division
a / b # true division with fractional part
int(1.75) # use this if you want to truncate the fractional part instead of flooring it.
a = True # boolean true
a = False # boolean false
boolean operators are : not, and, or
True/False are subclasses of integer
pi = 3.141 # real stored in 64 bits - double precision floating point format
c = 3.14 + 2.73j # complex number
c.real # 3.14
c.imag # 2.73
string, tuples, bytes
string # unicode code points
s = ""
s = ''
#multiline string below
s = """
"""
s = '''
'''
s_encoded = s.encode('utf-8') # utf-8 encoded
s_encoded.decode('utf-8')
s_bytes = b"A bytes object" # a bytes object
s = "arun"
s[0] # 'a'
s[:3] # 'aru' - s[start:stop:step] - stop is exclusive
s[:] # make a copy of original
s[::-1] # copy of original in reverse order
t = () # tuple, a sequence of arbitary python objects
t = (42, ) # need a comma for one element tuple
a, b, c = 3, 2, 1 # multiple assignments
2 in t # membership test
a, b = b, a # pythonic way to swap two variables
tuples can be used as keys in dictionaries
List, bytearray, set, dict
[] # empty list
list() # same as []
a = [1, 2, 3]
a = [x + 2 for x in [1, 2, 3]] # a = [3, 4, 5]
list((1, 2, 3)) # => [1, 2, 3] ; convert a tuple into a list
list('hello') # ['h', 'e', 'l', 'l', 'o'] ; list from string
methods on list:
a.append(5) # [3, 4, 5, 5]
a.count(5) # how many 5 are there in a
a.extend([6, 7]) # [3, 4, 5, 5, 6, 7]
a.index(4) # 1 ; position of 4 in [3, 4, 5, 5, 6, 7]
operations on list:
min(a) # 3
sum(a) # 30
bytearray:
name = bytearray(b'Arun')
str to bytearray:
s1 = "github"
# we cannot directly update s1 since it is immutable
b1 = bytearray(s1.encode("utf-8"))
b1[0] = ord("h")
s2 = b1.decode("utf-8")
print(s2) # prints "hithub"
set:
a = set() # set
a.add(1)
a.add(2)
a.add(3)
a.add(1)
#a = {1, 2, 3}
1 in a # True
1 not in a # False
frozenset([1, 2]) # immutable set
dict:
a = dict(A=1, Z=-1)
a = {'A': 1, 'Z': -1}
a = dict(zip(['A', 'Z'], [1, -1]))
a = dict([('A', 1), ('Z', -1)])
a = dict({'Z': -1, 'A': 1})
del a['Z'] # delete key 'Z'
a['B'] = 2 # add key B and value 2
'B' in a # True
a.clear()
a.keys()
a.values()
a.items()
Instead of below:
square = []
for n in range(10):
square.append(n * 2)
do below:
[n * 2 for n in range(10)]
Set comprehension is similar:
do below:
{n * 2 for n in range(10)}
Nested comprehension:
items = ['AB']
[(items[i], items[j]) for i in range(len(items) for j in range(len(items))]
#=>[(A, A), (A, B), (B, B)]
[[col for col in range(5)] for row in range(5)]
#=>[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
Filtering comprehension:
[n for n in range(10) if n % 2 == 0]
list(map(lambda x: x, range(3)))
#=> [0, 1, 2]
list(map(lambda * x: x, range(3), 'abc'))
#=> [(0, 'a'), (1, 'b'), (2, 'c')]
list(zip(['a', 'b'], [1, 2]))
#=> [('a', 1), ('b', 2)]
list(filter(None, [1, 0, 3, 4]))
#=> [1, 3, 4] ; None implies identitfy function, all values except False (0) is returned
Checkout python docs for more. https://docs.python.org/2/library/itertools.html
from itertools import permutations
print(list(permutations('ABC')))
#=> [('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]
Generator functions - they are similar to functions but instead of returning value, they yield which allows them to suspend and resume state between each call.
Generator expressions - they are similar to list comprehensions but instead of returning list, they return an object that produces results one by one.
def get_squares(n):
for x in range(n):
yield x ** 2
squares = get_squares(3)
print(next(squares)) # print 0
print(next(sqaures)) # print 1
print(next(sqaures)) # print 4
def counter(start=0):
n = start
while True:
result = yield n
if result == 'Q':
break
n += 1
c = counter()
print(next(c)) # 0
print(next(c)) # 1
print(c.send('Q')) # throws StopIteration
#=> generator expression
cubes_gen = (k ** 3 for k in range(10)) # this is a generator expression
#=> (k ** 3) above can be replaced with any type of lambda or def function
if income < 1000:
#do something
elif income < 2000:
#do something else
else:
#do entirely different thing
#ternary operator
discount = 25 if order_total > 101 else 0
for number in [1, 2, 3]:
print number
for key in dict:
# keys printed in arbitrary order
print dict[key]
while loop:
reminders = []
while n > 0:
remainder = n % 2
remainders.append(remainder)
n //= 2
remainders = remainders[::-1]
print(remainders)
for else:
if for loop is not exited via break then it will go to else clause
class DriverException(Exception):
pass
people = [('Arun', 3), ('Kirk', 9)]
for person, age in people:
if age >= 18:
break
else:
raise DriverException('Driver not found.')
Any class that defines __iter__()
and __getitem__()
method can be used as iterators.
Iterator is an object capable of returning its value one at a time.
def func(a, b, c):
print(a, b, c)
func(a=1, c=3, b=2)
func(1, 3, 3)
def func2(a=10):
print a
func2()
func2(11)
variable positional arguments
#can be used to pass variable number of positional argument
def min(*args):
# args is a tuple now
if args:
mn = args[0]
for value in args[1:]:
if value < mn:
mn = value
print mn
min(1, 3, 4, -1)
values = (1, 3, 4, -1)
min(*values) # unpacking the tuple to pass it as separate params
variable keyword argument
similar to variable positional argument but uses ** syntax and collects the parameters in a dict
def func(**kwargs):
print kwargs
func(a=1, b=10)
func(**dict(a=1, b=10))
Avoid mutable defaults arguments
Return multiple values possible
Anonymous function (lambda)
lambda x: x
lambda _: print 'hi'
A pattern to add extra functionality to a function in a DRY manner
from time import sleep, time
from functools import wraps
def measure(func):
@wraps(func)
def wrapper(*args, **kwargs):
t = time()
func(*args, **kwargs)
print(func.__name__, 'took:', time() - t)
return wrapper
@measure
def f(sleep_time=0.1):
"""I'm a dog and I love sleeping!"""
sleep(sleep_time)
f(sleep_time=0.3)
print(f.__name__, ':', f.__doc__)
Decorator factory passes arguments to decorator to modify its behavior dynamically
class Book:
pass
#Ebook extends Book
class EBook(Book):
pass
class EBook(Book):
def __init__(self, title, format):
Book.__init__(self, title)
self.format = format
Advanced (when required) - google or refer to the 'Learning Python' book
static methods class methods private and name mangling property decorator operator overloading multiple inheritance custom iterator
try:
# some code
except Exception1:
# react to Exception1
except (Exception1, Exception2):
# react to Exception1 and Exception2
except Exception3:
# react to Exception3
from typing import <types>
variable_name : type_name
# function param
def func1(x: type1) -> type2:
pass
poor man's unit testing of module
if __name__ == '__main__':
funcToTest()
print statments
inspect tracebacks
pdb - python debugger
# From here - https://stackoverflow.com/a/8437583
$ python -m py_compile your_script.py