Function code and data that performs some subtask of the overall task of the program. chief technique to deal with complexity of software: decompose problem into subproblems. ("decomposition") Function as a module/unit/procedure that can be plugged in anywhere needed in same program or in different programs. Avoiding duplication of code. Can use it without needing to know the details of how it works ("abstraction"). "Definition": the code and local data variables of the function. "Call": invoking/using/calling the function, and passing it "arguments" Built-in functions' (print(), input(), islower(), len(), math.sqrt(), etc) definition was made by Python system writers. For your own functions, you write the definition. Ex. an exponentiation function: let's name it power, pass it base x and exponent n (multiply x by itself n times). Ignore that there is the ** operator and math.pow(). "Parameters" are the x and the n. Return value is x to the nth power. Definition: def power(x, n): p = 1 for i in range(n): p = p * x #(exponentiation is shortcut for repeated multiplication) return p Notes: function name is an identifier. Must obey rules for identifiers. Use meaningful names. x and n are the "parameters". Can be used as local variables in the body of the function. Are assigned the values of the actual arguments in the call. Values of the "actual arguments" are passed in to become the values of the parameters. Changing the parameters will not change the actual arguments in the call of the function, [exception: a list or dict will change.] i and p are "local variables" of the function. They are not accessible by other functions or the main program. They come into existence each time the function is called. They do not keep their values from the last call of the function. The parameters are essentially local variables that are initialized by the actual arguments. return statement is the value returned by/from the function. Typically the last statement in a function, but could have more than one in a function. When executed the function is finished. Is optional, i.e. "void" function returns no (explicit) value, returns None (indicating absence of a value). [Can return more than one value, as a tuple.] Want: table of powers of 2 and 3 i 2^i 3^i 1 2 3 2 4 9 3 8 27 4 16 81 5 32 243 ... Use the power function (ie. call/invoke it): Must be defined before called. num_rows = int(input("Enter the number of rows of the power table: ")) for i in range(num_rows): print(i, power(2,i), power(3,i)) ********************************************************** "Scope" of variables: def myFunc(x,y): print("myFunc called x:",x," y:",y) x = 3 #Not the global x y = 4 #Not the global y print("myFunc called x:",x," y:",y) z = 5 #"local" variable, doesn't exist outside of this function print("myFunc called w:",w) #can access the global w #w += 1 #Error. but can NOT modify the global w. x = 1 y = 9 w = 1 print("before call. x:",x," y:",y) myFunc(x,y) print("after call. x:",x," y:",y) # 1 9 #print(z) #error. Can NOT "look inside" a function. [Use of the "global" keyword modifies this. A variable declared global inside a function can modify a global variable or create existence globally.] If argument is a list, the function can change it. The parameter is a reference to the list object. def f(list_param): list_param[0] = 999 list_arg = [1,2,3,4,5] print(list_arg) f(list_arg) print(list_arg) ********************************************************** Default values and keyword parameters: def cm(feet=0, inches=0): inches_to_cm = inches * 2.54 feet_to_cm = feet * 12 * 2.54 return feet_to_cm + inches_to_cm print(cm(feet=2,inches=10)) print(cm(inches=10,feet=2)) #keyword args in any order print(cm(feet=2)) #inches will be given default value print(cm(inches=10)) print(cm()) print(cm(5,7)) Keyword arguments without default values: def cm(feet, inches): ... print(cm(inches=10,feet=5)) #can be any order ********************************************************** Arbitrary parameters: any number of arguments, as a tuple def f(*args): for arg in args: #loop over them for demo print(arg, end=" ") print() f(123,"helo",34.56) f(10,20,30,40,50,60) f(0, ['ab','c']) #positional parameter, keyword w/default, arbitrary: def f(x, y=10, *args): print(x, y) for a in args: print(a) f(23) f(23,34) f(23,34,12,45) ********************************************************** Recursive function, calls itself def factorial(n): if n==1: return 1 else: return n * factorial(n-1) print(factorial(5)) print(factorial(10)) def ulam(n): print(n, end=" ") if n == 1: pass elif n%2 == 0: #n even, call again with half of n n = n // 2 #avoid float result . ulam(n) else: #n odd, call again with 3n+1 n = 3 * n + 1 ulam(n) ulam(5) print() ulam(12) print()