Function
Pass by reference
- All parameters (arguments) in the Python language are passed by reference
- Mutable parameters, can be modified by function
- Immutable parameters, cannot be modified by function
# pass by reference
def f2(m, im):
print('----------------Inside function-------------')
print(id(m), m) # 140426914615040 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(id(im), im) # 140426908592720 10
m.append(100);
im = 100;
print(id(m), m) # 140426914615040 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100]
print(id(im), im) # 140426908784080 100
print('---------------Leave function--------------')
a = 10; # 38097024 10
b = list(range(10)) # 13977378998840, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,]
print(id(a), a) # 140426908592720 10
print(id(b), b) # 140426914615040 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
f2(b, a);
print(id(a), a) # 140426908592720 10
print(id(b), b) # 140426914615040 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100]
Default arguments
def f1(p1, p2 = 10):
print(p1, p2)
f1(1) # 1 10
Keyword arguments
def f3(p1 = 1, p2 = 2):
print(p1, p2)
f3(p2 = 10, p1 = 100) # 100 10
# use keyword-only arguments
# * separate positional/keyword arguments and keyword-only arguments
def f2(p1, p2 = 10, *, p3 = 100):
print(p1, p2, p3)
# f2(1, 2, 1000) # error, pass positional argument to a keyword-only argument
f2(1, 2, p3 = 1000)
Variable-length arguments
def f4(*vartuple):
print('Lenght: ', len(vartuple)) # 4
print(type(vartuple), vartuple) # (1, 2, 3, 'end'), vartuple is a tuple
for i in vartuple:
print(i)
f4(1, 2, 3, 'end')
* in Function Calls
expands list or tuple into separate elements
def f(a, b):
print(a, b)
l = [10, 20] # singularize list or tuple
f(*l) # 10 20
Arbitary Key Parameters
pass parameters and their values as a dict to function
def f(**args):
print(args)
for k in args.keys():
print(k, args[k])
f(inFile = 'input', outFile = 'output')
** in Function Calls
pass keyworded variables as a dict to function
def f(**args):
print(args)
for k in args.keys():
print(k, args[k])
d = {'inFile':'input', 'outFile':'output'} # singularize dict
f(**d)
def f(inFile, outFile):
print(inFile, outFile)
d = {'inFile':'input', 'outFile':'output'}
f(**d)
Lambda
#!/usr/bin/python
#lambda
sum = lambda arg1, arg2: arg1+arg2
print(sum(10, 20)) #30
#map
def t(arg):
return arg*10
l = [1, 2, 3, 4]
r = map(t, l) # [10, 20, 30, 40], use function
r = map(lambda x: x*10, l) #[10, 20, 30, 40], using lambda
#filter
r = filter(lambda x: x%2 == 0, l) # [2, 4]
#reduce
from functools import reduce
r = reduce(lambda x, y: x+y, l) # 10
Return
return multiple values, which are saved in a tuple
def getNums():
return 1, 2
a, b = getNums()
print(a, b) # 1, 2
_, b = getNums() # use _ to hold a place
print(b) # 2
t = getNums()
print(t) # (1, 2), t is a tuple
def getNum_1():
return 1 # return an integer
def getNum_2():
return # return nothing
def getNum_3():
return None # return None
def getNum_4():
pass # no return will return None
def main():
if getNum_1() is None:
print(getNum_1()) # not print
if getNum_2() is None:
print(getNum_2()) # None
if getNum_3() is None:
print(getNum_3()) # None
if getNum_4() is None:
print(getNum_4()) # None
if __name__ == '__main__':
main()
Recursive
# recursive
def fib(n):
"""Doc String
Args:
n (int), integer number
Return:
int, fib number
"""
if n <= 1:
return 1
else:
return n*fib(n-1)
print(fib(10))
Functools
functools.partial, is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature
from functools import partial
def f(a, b):
print(a, b)
p = partial(f, b = 10)
p(1) # 1 10, pass 1 to a, p() only need one argument
functools.update_wrapper, to allow access to the original function for introspection and other purposes
from functools import partial, update_wrapper
def f(a, b):
""" Docstring for f ..."""
print(a, b)
p = partial(f, b = 10)
p(1) # 1 10, pass 1 to a, p() only need one argument
#print(p.__doc__, p.__name__) # partial object does not have __name__ or __doc__ attributes by default
update_wrapper(p, f) # copies or adds attributes from the original function to the partial object
print(p.__doc__, p.__name__)
functools.partialmethod, returns a callable ready to be used as an unbound method of an object
from functools import partialmethod
def getInfo(self, a, b):
"""a partial function in class"""
print(' called standalone with:', (self, a, b))
if self is not None:
print(' self.attr =', self.attr)
class MyClass:
"Demonstration class for functools"
def __init__(self):
self.attr = 'instance attribute'
method = partialmethod(getInfo, b = 10)
c = MyClass()
c.method(1)
functools.wraps, updating the properties of a wrapped callable when used in a decorator
# use decorator
def d(f):
def decorated(a=10, b=100):
return f(a, b=b)
return decorated
@d
def f(a, b):
""" Docstring for f ..."""
print(a, b)
dwrapper = d(f)
dwrapper() # 10 100
print(dwrapper.__doc__, dwrapper.__name__) # None decorated
f(b = 1) # 10 1
# use wraps() to define a decorator
from functools import wraps
def d(f):
@wraps(f)
def decorated(a=10, b=100):
return f(a, b=b)
return decorated
def f(a, b):
""" Docstring for f ..."""
print(a, b)
dwrapper = d(f)
dwrapper() # 10 100
print(dwrapper.__doc__, dwrapper.__name__) # Docstring for f ... f
functools.lru_cache, wraps a function in a least-recently-used cache, subsequent calls with the same arguments will fetch the value from the cache instead of calling the function
from functools import lru_cache
@lru_cache()
def f(a, b):
print('%d, %d' % (a, b))
return a * b
f(1, 10)
print(f.cache_info()) # print out the cache information
f.cache_clear() # clear cache
Six rich class comparison functions, __lt__, __le__, __gt__, __ge__, __eq__, __ne__
class Vehicle(object):
def __init__(self, year: int):
self._year = year
@property
def year(self) -> int:
return self._year
@year.setter
def year(self, y : int) -> None:
self._year = y
def __eq__(self, other):
return self._year == other._year
def __lt__(self, other):
return self._year < other._year
v1 = Vehicle(1999)
v2 = Vehicle(2022)
print(v1.__lt__(v2)) # True
print(v1.__le__(v2)) # NotImplemented
print(v1.__gt__(v2)) # NotImplemented
print(v1.__ge__(v2)) # NotImplemented
print(v1.__eq__(v2)) # False
print(v1.__ne__(v2)) # True
functools.total_ordering Provides rich class comparison methods that help in comparing classes without explicitly defining the six rich comparison function
At least one of the comparison methods must be defined from lt(less than), le(less than or equal to), gt(greater than) or ge(greater than or equal to)
The eq function must also be defined
from functools import total_ordering
@total_ordering
class Vehicle(object):
def __init__(self, year: int):
self._year = year
@property
def year(self) -> int:
return self._year
@year.setter
def year(self, y : int) -> None:
self._year = y
def __eq__(self, other):
return self._year == other._year
def __lt__(self, other):
return self._year < other._year
v1 = Vehicle(1999)
v2 = Vehicle(2022)
print(v1.__lt__(v2)) # True
print(v1.__le__(v2)) # NotImplemented
print(v1.__gt__(v2)) # NotImplemented
print(v1.__ge__(v2)) # NotImplemented
print(v1.__eq__(v2)) # False
print(v1.__ne__(v2)) # True
Overload
Python does not have real function overload, either use overloading module or isinstance to check and handle different type of inputs
def info(m):
if isinstance(m, str):
return 'Str: '+m
elif isinstance(m, int):
return 'Int: '+str(m)
else:
raise TypeError('Take str or int types ...')
print(info(100))
print(info('Hello'))
print(info(3.14)) # raise error
# use default value to work as overloaded function
def info(a, b = None):
if b is None:
return a
return a+b
print(info(1, 2)) # 3
print(info(1)) # 1
Function Attribute
def func():
print('Hello World!')
# create an attribute for the function
func.record = 'initial record'
#['__annotations__', ..., __str__', '__subclasshook__', 'record']
print(dir(func))
def func():
func.call += 1
print('Hello World!')
# count how many times the function is called
func.call = 0
func()
func()
print(func.call) # 2
Reference