Notes from lecture 11, which was devoted to answering questions and to practice of evaluating function calls. If you understand how Python executes programs, you will be better at writing Python programs, and you will be much better at understanding and debugging Python programs. Our goal is to prevent you from getting stuck when your program does not work right. If your program does not do what you think it should do, then a good strategy is to play computer and execute your program manually. This will help you to understand where your beliefs about your program differ from reality. Then you can fix the bug and move on without getting deeply frustrated. Functions are values def double(x): return x+x double # The Python representation indicates that the function is something # different than an int, float, list, etc. list1 = [double, math.abs] # This expression is a function call. One of its subexpressions is a list # dereference (also known as list indexing). mylist[0](7) mylist[1](-22) A function call can return a function: def doubler(): return double doubler()(3) A more practical example is itemgetter: import operator operator.itemgetter(2, 7, 9, 10) operator.itemgetter(2, 7, 9, 10)("dumbstricken") operator.itemgetter(2, 5, 7, 9)("homesickness") operator.itemgetter(2, 7, 9, 10)("pumpernickel") operator.itemgetter(2, 3, 6, 7)("seminaked") operator.itemgetter(1, 2, 4, 5)("smirker") operator.itemgetter(9, 7, 6, 1)("beatnikism") operator.itemgetter(14, 13, 5, 1)("Gedankenexperiment") operator.itemgetter(12, 10, 9, 5)("mountebankism") Recall the rules for function call evaluation that we have seen a few times before in lecture: * evaluate the function expression to a value * evaluate the argument expressions to values (from left to right) * if the function value is not a function, abort * create a new stack frame ** this is also known as an environment frame ** an "environment" is all the stack frames, in order * assign each formal parameter variable to each actual argument value * evaluate the body of the function * at a return statement: * remember the value * erase the stack frame * the value of the function call is the return value How to evaluate an assignment: * The assignment always happens in the current stack frame, never in a higher stack frame such as the global stack frame. ** Python supplies a way to override this, but we won't discuss it and you should avoid it. How to evaluate a variable: * Look in the current stack frame. * If found, that is the value. * Otherwise, proceed to the previous stack frame and try again. * If not found in the global frame, then fail with an error. Assignments are evaluated differently than expressions. For most expressions, the rule is: * evaluate the left-hand side * evaluate the right-hand side * perform the math operation For assignments, the left-hand side is a variable name, not an expression. The rule is: * evaluate the right-hand side * assign to the left-hand side variable Terminology: * a function definition contains variables, called "formal parameters" * a function call expression contains expressions, called "actual arguments" Don't mix these up! Practice with evaluating function call expressions, such as double(7) and abs(-20 - 2) + 20 What is this "lambda" thing we have heard about? Recall that we know about many Python types, and we know how to create a value of those types. For example: Type Expression of that type int 22 42 float 3.14 1.0 6e23 list [1,2,3] [] set {1,2,3} set([]) function ??? We don't have a way of creating a function value directly. We can create function values and bind them to a name, like this: def double(x): return x + x and then we can access the function value by writing double This is a tiny bit inconvenient. It's as if I couldn't write an int or list value directly, but only in a variable assignment; instead of writing 3 + 4 I had to write a = 3 b = 4 a + b It turns out that there *is* a way to directly write a function value, without assigning it to a name/variable. That way is called "lambda". You would only use this for a function that you wanted to use exactly once. If you want to use a function more than once, you should just give it a name and use that name. Lambda is useful, but it is an advanced concept. We will not learn about lambda yet. We may learn about it later in the quarter. dictionary evaluation How to evaluate a list or dictionary dereference/indexing expression, such as mycollection[myindex] : * evaluate the collection expression to a value * evaluate the index expression to a value * if the collection value isn't a collection (such as a list or set or tuple), abort with an error * use the index to select which element of the collection to return. This works differently depending on the type of the collection. ** for a list: count that many elements from the beginning *** if the index is not an integer, or is out of range, abort with an error ** for a dictionary, look for that key and return the corresponding value *** if the key does not appear in the dict, abort with an error squares = { 1: 1, 5: 25, 6: 36 } squares[5] + squares[5] squares[5+5]