Function
Pass by reference
# pass by reference
def f2(m, im):
    print('----------------Inside function-------------')
    print(id(m), m) # 13977378998840, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(id(im), im) # 38097024, 10
    m.append(100);
    im = 100;
    print(id(m), m) # 13977378998840, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100]
    print(id(im), im) # 38098848, 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)
print(id(b), b)

f2(b, a);
print(id(a), a) # 38097024, 10
print(id(b), b) # 13977378998840, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100]
			
Default arguments
# default arguments
def f1(p1, p2 = 10):
    print(p1, p2)

f1(1)
			
Keyword arguments
# keyword arguments
def f3(p1 = 1, p2 = 2):
    print(p1, p2)

f3(p2 = 10)
			
Variable-length arguments
# variabel-length arguments
def f4(*vartuple):
    print('Lenght: ', len(vartuple))
    print(type(vartuple), vartuple) # 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
  • #!/usr/bin/python
    
    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
  • #!/usr/bin/python
    
    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
  • #!/usr/bin/python
    
    def f(**args):
        print(args)
        for k in args.keys():
            print(k, args[k])
    
    d = {'inFile':'input', 'outFile':'output'}; # singularize dict
    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
    print(list(r))
    r = map(lambda x: x*10, l) #[10, 20, 30, 40], using lambda
    print(list(r))
     
    #filter
    r = filter(lambda x: x%2 == 0, l) #[2, 4]
    print(list(r))
     
    from functools import reduce
    #reduce
    r = reduce(lambda x, y: x+y, l) #10
    print(r)
    			
    Return
  • return multiple values, which are saved in a tuple
  • #!/usr/bin/python
     
    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
    			
    #!/usr/bin/python
    
    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
    #!/usr/bin/python
     
    # 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
  • #!/usr/bin/python
    
    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
  • #!/usr/bin/python
    
    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
  • #!/usr/bin/python
    
    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
    #!/usr/bin/python
    
    # 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
    			
    #!/usr/bin/python
    
    # 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
  • #!/usr/bin/python
    
    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
    			
    Overload
  • Python does not have real function overload, either use overloading module or isinstance to check and handle different type of inputs
  • #!/usr/bin/python
    
    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
    			
    #!/usr/bin/python
    
    # 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
    			
    Reference