Docstrings
help(print)
def my_func(a, b):
return a * b
help(my_func)
def my_func(a, b):
'Returns the product of a and b'
return a * b
help(my_func)
def my_func(a, b):
'''Returns the product of a and b'''
return a * b
help(my_func)
def fact(n):
'''Calcalates n! (factorial funtion)
Input:
n: non-negative integer
Return:
the factorial of n
TODO:
this is the slowest possible factorial function, let's change this in future
'''
# bible.. game of thrones...
if n < 0:
'''Note that this is not a part of doctring!'''
return 1
else:
return n * fact(n-1)
help(fact)
fact.__doc__
help(fact)
Annotations
def my_func(a, b):
return a ** b
my_func('rohan', 'shravan')
def my_func(a: 'please keep this as an integer',
b: 'please keep this also as an integer'):
'''this function just
gives a^b as the output'''
return a ** b
help(my_func)
x = 3
y = 5
def my_func(a: 'a'*5) -> '"a" repeated ' + str(max(x, y)) + ' times':
'''this is the documentition
of our function'''
return a * max(x, y)
help(my_func)
my_func(0.4)
my_func.__doc__
help(my_func)
my_func.__annotations__
def fact(n: 'int or float >=0'=1) -> int:
'''Calcalates n! (factorial funtion)
Input:
n: non-negative integer
Default: 1
Return:
the factorial of n
ToDo:
this is the slowest possible factorial function, let's change this in future
'''
# bible.. game of thrones...
if n < 0:
'''Note that this is not a part of doctring!'''
return 1
else:
return n * fact(n-1)
help(fact)
fact.__annotations__
def my_func(a: str='a', b: int = 1) -> str:
return a * b
help(my_func)
my_func(a= 'r', b = 4)
def my_func(a:int=0, *args:'additional args'):
print(a, args)
my_func.__annotations__
help(my_func)
def my_func(a: int):
pass
print(my_func.__annotations__)
def my_func(a: 'int'):
pass
print(my_func.__annotations__)
Lambda Expressions
def my_func(x):
print( x ** 2)
my_func(4)
f = lambda x: print(x ** 2)
f(4)
def my_func(f, n):
return f(n)
my_func(lambda x: 2 * x, 14)
lambda x: print(x ** 2)
func = lambda x: print(x ** 2)
type(func)
func(4)
func_1 = lambda x, y = 10 : (x, y)
func_1(1, 2), func_1(10)
func_2 = lambda x, *args, y, **kwargs: (x, *args, y, {**kwargs})
func_2(1, 'a', 'b', y=100, a = 10, b = 24, c = 'python')
def apply_func(x, fn):
return fn(x)
apply_func(3, lambda x: x**2)
def apply_fn(fn, *args, **kwargs):
return fn(*args, **kwargs)
apply_fn(lambda x, y: x + y, 1, 2)
apply_fn(lambda x, *, y: x + y, 1, y = 2)
apply_fn(lambda *args: sum(args), 1, 2, 3, 4, 5, 6, 7, 8)
apply_fn(sum, (1, 2, 3, 4, 5, 6, 7, 8))
def mulitply(x, y):
return x * y
apply_fn(mulitply, '1', 7)
Lambdas and Sorting
l = ['a', 'B', 'c', 'D']
sorted(l)
sorted(l, key = str.upper)
sorted(l, key = lambda s: s.upper())
d = {'def': 300, 'abc': 200, 'ghi': 100, 'jlk': 50}
d
sorted(d)
sorted(d, key = lambda k: d[k])
def dist(x):
return (x.real)**2 + (x.imag)**2
l = [3+3j, 1+1j, 0+0j]
sorted(l)
sorted(l, key=dist)
sorted(l, key = lambda x: (x.real)**2 + (x.imag)**2)
l = ["Obama", "Hillary", "Modi", "Ajit", "Rahul", "Bernie"]
sorted(l)
sorted(l, key = lambda x:x[-1])
Function Introspection
def fact(n: "some non-negative integer") -> "n! or 0 if n < 0":
'''Calcalates n! (factorial funtion)
Input:
n: non-negative integer
Default: 1
Return:
the factorial of n
or
0 if n is negative
ToDo:
this is the slowest possible factorial function, let's change this in future
'''
# bible.. game of thrones...
if n < 0:
'''Note that this is not a part of doctring!'''
return 0
elif n <= 1:
return 1
else:
return n * fact(n-1)
fact.short_description = "factorial function"
print(fact.short_description)
dir(fact)
fact.long_description = "this is a long descroipton writeen by shashi tharoor which is not ending today"
dir(fact)
fact.__dir__()
def my_func(a, b = 2, c = 3, *, kw1, kw2= 2, **kwargs):
pass
f = my_func
my_func.__name__
f.__name__
my_func.__defaults__
help(print)
my_func.__kwdefaults__
def my_func(a, b = 1, *args, **kwargs):
i = 10
b = min(i, b)
return a * b
my_func('a', 100)
my_func.__code__
dir(my_func.__code__)
my_func.__code__.co_varnames
my_func.__code__.co_argcount
Inspect Module
import inspect
inspect.isfunction(my_func)
inspect.ismethod(my_func)
class MyClass:
def f_instance(self):
'Instance methods are bound to the instance/object of the class'
pass
@classmethod
def f_class(cls):
'Class method are bound to the CLASS, not instances'
pass
@staticmethod
def f_static():
'Statis methods are not bounded to either class or its objects/instances'
pass
inspect.isfunction(MyClass.f_instance), inspect.ismethod(MyClass.f_instance),
inspect.isfunction(MyClass.f_class), inspect.ismethod(MyClass.f_class),
inspect.isfunction(MyClass.f_static), inspect.ismethod(MyClass.f_static),
my_obj = MyClass()
inspect.isfunction(my_obj.f_instance), inspect.ismethod(my_obj.f_instance),
inspect.isfunction(MyClass.f_instance), inspect.ismethod(MyClass.f_instance),
inspect.isfunction(my_obj.f_class), inspect.ismethod(my_obj.f_class),
inspect.isfunction(my_obj.f_static), inspect.ismethod(my_obj.f_static),
inspect.isroutine(my_func)
inspect.isroutine(MyClass.f_instance)
inspect.isroutine(MyClass.f_class)
print(inspect.getsource(fact))
print(inspect.getsource(MyClass.f_instance))
inspect.getmodule(fact)
inspect.getmodule(print)
import math
inspect.getmodule(math.sin)
# setting up variable
i = 10
# comment line 1
# comment line 2
def my_func(a, b = 1):
# comment inside my_func
pass
inspect.getcomments(my_func)
print(inspect.getcomments(my_func))
# TODO: Provide implementation
def my_func(a: 'a string',
b: int = 1,
*args: 'additional argument args',
kw1: 'first keyword only arg',
kw2: 'second keyword only arg',
**kwargs: 'additional keyword only args') -> str:
"""does something
or ther other"""
pass
inspect.signature(my_func)
type(inspect.signature(my_func))
sig = inspect.signature(my_func)
dir(sig)
for param_name, param in sig.parameters.items():
print(param_name, param)
def print_into(f: "callable") -> None:
print(f.__name__)
print('=' * len(f.__name__), end = '\n\n')
print(f'{inspect.getcomments(f)}\n{inspect.cleandoc(f.__doc__)}')
print(f'Inputs\n{"-"*len("Inputs")}')
sig = inspect.signature(f)
for param in sig.parameters.values():
print("Name:", param.name)
print("default:", param.default)
print("annotation:", param.annotation)
print("kind:", param.kind)
print('----------------------------\n')
print(f'\n\nOutput {"-"*len("Output")}')
print(sig.return_annotation)
print_into(my_func)
help(divmod)
divmod(10, 3)
divmod(x = 10, y = 3)
'rohan'.replace('ro', 'mo')
help(str.replace)
'rohan'.replace(old = 'ro', new = 'mo')
callable(print)
callable(len)
l = [1, 2, 3]
callable(l.append)
s = 'abc'
callable(s.upper)
result = print('hello')
print(result)
l = [1, 2, 3]
result = l.append(4)
print(result)
print(l)
s = 'abc'
result = s.upper()
print(result)
from decimal import Decimal
callable(Decimal)
result = Decimal('10.5')
print(result)
class MyClass:
def __init__(self):
print('initialzing ... ')
self.counter = 0
def __call__(self, x = 1):
self.counter += x
print(self.counter)
my_obj = MyClass()
callable(my_obj.__call__)
my_obj()
my_obj(10)
class MyClass:
def __init__(self):
print('initialzing ... ')
self.counter = 0
my_obj = MyClass()
my_obj()
Higher-Order Functions: Map and Filter
help(map)
def fact(n):
return 1 if n < 2 else n * fact(n-1)
fact(3)
map(fact, [1, 2, 3, 4, 5])
list(map(fact, [1, 2, 3, 4, 5]))
l1 = [1, 2, 3, 4, 5]
l2 = [10, 20, 30, 40, 50]
f = lambda x, y: x + y
m = map(f, l1, l2) #no computation, just a lasy object
list(m) # here computation happens
# map which gives sq or a list [1, 2, 3, 4] >> [1, 4, 9, 16]
list(map(lambda x: x**2, [1, 2, 3, 4]))
help(filter)
l = [0, 1, 1, 0, 1, False, True]
list(filter(None, l))
l = [0, 1, 1, 0, 1, False, True] # [-1, 0, 0, -1, 0, -1, 0]
list(filter(lambda x: x - 1, l)) # [0, 0, False]
bool(-1)
l = [0, 1, 2, 3, 4, 5, 6]
for e in filter(None, l): # for e in range(10)
print(e)
l = [0, 1, 2, 3, 4, 5, 6]
list(filter(lambda x: x%2 == 0, l))
# map and filter
# these are obsolete!!
# LC instead of Maps
l = [1, 2, 3, 4, 5]
[fact(i) for i in l]
# LC replacement for Filter
[fact(i) for i in l if fact(i)%2 == 0]
whatisthis = (fact(i) for i in l)
for e in whatisthis:
print(e)
for e in whatisthis:
print(e)
r = range(10)
for k in r:
print(k)
for k in r:
print(k)
l1 = 1, 2, 3
l2 = ['a', 'b', 'c']
list(zip(l1, l2))
l1 = 1, 2, 3
l2 = ['a', 'b', 'c', 'd']
list(zip(l1, l2))
l1 = 1, 2, 3
l2 = ['a', 'b', 'c', 'd']
l3 = (1, 2, 3, 4, 5, 6, 7, 8)
list(zip(l1, l2, l3))
l1 = range(100000000000)
l2 = 'python'
list(zip(l1, l2))
l1 = [1, 2, 3, 4, 5]
l2 = [10, 20, 30, 40, 50]
[z1+ z2 for z1, z2 in zip(l1, l2)]
[z1+ z2 for z1, z2 in zip(l1, l2) if (z1 + z2)%2 == 0]
[x**2 for x in range(10) if x**2 < 25]
list(filter(lambda y: y< 25, map(lambda x: x**2, range(10))))
Reducing Functions
l = [1, 2, 3, 4, 5 ]
max(l), min(l)
_max = lambda a, b: a if a>b else b
def max_sequence(sequence):
result = sequence[0]
for x in sequence[1:]:
result = _max(result, x)
return result
max_sequence(l)
_min = lambda a, b: a if a<b else b
def min_sequence(sequence):
result = sequence[0]
for x in sequence[1:]:
result = _min(result, x)
return result
min_sequence(l)
def _reduce(fn, sequence):
result = sequence[0]
for x in sequence[1:]:
result = fn(result, x)
return result
_reduce(_max, l), _reduce(_min, l)
_reduce(lambda a, b: a if a > b else b, l)
sum(l)
_reduce(lambda a, b: a + b, l)
from functools import reduce
l
reduce(lambda a, b: a if a > b else b, l)
reduce(lambda a, b: a + b, l)
any and all
l = [0, 0.01, False]
any(l)
l = [True, 0.01, -1231231, None]
all(l)
l = [0, 1, 2]
reduce(lambda a, b: bool(a or b), l) # any
l = [0, 1, 2]
reduce(lambda a, b: bool(a and b), l) # all
l = [1, 2, 3, 4, 5, 6]
reduce(lambda a, b: a * b, l)
# use reducing function to calcualate factorial of 5
n = 5
reduce(lambda a, b: a*b, range(1, n + 1))
l = [1, 2, 3]
reduce(lambda x, y: x*y, l)
l = []
reduce(lambda x, y: x*y, l)
l = []
reduce(lambda x, y: x*y, l, 1)
Partial Function
from functools import partial
def my_func(a, b, c):
print(a, b*b, c)
f = partial(my_func, 10)
f(4, 20)
fn = lambda b, c: my_func(10, b, c)
fn(4, 20)
def my_func(a, b, *args, k1, k2, **kwargs):
print(a, b, args, k1, k2, kwargs)
f = partial(my_func, 10, k1='a')
f(20, 30, 40, 50, k2 = 'rohan', l=123, o=12312)
def power(base, exponent):
return base ** exponent
power(2, 3)
square = partial(power, exponent=2)
square(4)
cube = partial(power, exponent=3)
cube(4)
cube(base = 2)
def my_func(a, b, c):
print(a, b, c)
a = 10
f = partial(my_func, a)
f(20, 30)
a = 100
f (20, 30)
a = [10, 20]
f = partial(my_func, a)
f(100, 20)
a = [10, 20, 30]
f(100, 20)
a.append(400)
f(100, 20)
a = [10, 20]
f = partial(my_func, a)
f(100, 20)
a.append(300)
f(100, 20)
a = [10, 20]
id(a)
a = [10, 20, 30]
id(a)
a.append(1234)
id(a)
origin = (0, 0)
l = [(1, 1), (0, 2), (-3, 2), (5, 4), (0, 0)]
dist2 = lambda x, y: (x[0] - y[0])**2 + (x[1] - y[1])**2
dist2((0, 0), (1, 1))
sorted(l, key = lambda x: dist2((0, 0), x))
sorted(l, key = partial(dist2, (0, 0)))
Operator Module
import operator
dir(operator)
operator.add(1, 2)
operator.mul(2, 3)
operator.pow(2, 4)
operator.floordiv(13, 2)
operator.truediv(13, 2)
reduce(lambda x, y: x*y, [1, 2, 3, 4,5 ])
reduce(operator.mul, [1, 2, 3, 4,5 ])
operator.lt(10, 100)
operator.le(10, 100)
operator.is_('abd', 'def')
operator.truth([1, 2])
operator.truth([])
my_list = [1, 2, 3, 4,5 ]
my_list[4]
operator.getitem(my_list, 4)
my_list[0] = 'rohan'
my_list
del my_list[0]
my_list
operator.delitem(my_list, 2)
my_list
class MyClass:
def __init__(self):
self.a = 10
self.b = 20
self.c = 30
def test(self):
print('test method running...')
obj = MyClass()
obj.a, obj.b, obj.c
f = operator.attrgetter('a')
f(obj)
obj2 = MyClass()
obj2.a = 100
f(obj), f(obj2)
l1 = [1, 2, 3, 4, 5, 6, 7, 8]
l2 = ['a', 'b', 'c']
list(zip(l1, l2*2))