A Function with Behavior That Varies Over Time Let's model a bank account that has a balance of $100 Argument: amount to Return value: >>> withdraw(25) withdraw remaining balance 75 >>> withdraw(25) 61A Lecture 11 Second withdrawal 50 Different of the same amount return value! >>> withdraw(60) 'Insufficient funds' Friday, September 23 >>> withdraw(15) Where's this 35 balance stored? >>> withdraw = make_withdraw(100) Within the function! 2 Persistent Local State Local State via Non-Local Assignment def make_withdraw(balance): """Return a withdraw function with a starting balance.""" make_withdraw: withdraw: def withdraw(amount): Declare the name make_withdraw (balance): "balance" nonlocal nonlocal balance function body to be revealed if amount > balance: momentarily return 'Insufficient funds' balance: 100 Re-bind the balance = balance - amount withdraw: existing balance name above return balance withdraw (amount): return withdraw function body to be revealed momentarily Demo 3 4 Local, Non-Local, and Global Frames The Effect of Nonlocal Statements nonlocal <name> , <name 2>, ... The global Effect : Future references to that name refer to its frame pre-existing binding in the first non-local frame of the current environment in which that name is bound. Python Docs: an Non-local "enclosing scope" Second frame frame From the Python 3 language reference : Non-local First non- An environment Names listed in a nonlocal statement must refer to frame local frame pre-existing bindings in an enclosing scope. Names listed in a nonlocal statement must not collide First Local with pre-existing bindings in the local scope. frame frame http://docs.python.org/release/3.1.3/reference/simple_stmts.html#the-nonlocal-statement http://www.python.org/dev/peps/pep-3104/ 5 6
The Many Meanings of Assignment Statements Assignment Review: Teenage Mutant Ninja Turtles x = 2 mutant: mutant(y): y, x = y+1, y+2 Status Effect ninja: return ninja(y)/2 turtle: • No nonlocal statement Create a new binding from name "x" • "x" is not bound locally y: 5 to object 2 in the first frame of ninja(x): the current environment. return x + 2 • No nonlocal statement Re-bind name "x" to object 2 in the x: 6 y: 5 6 • "x" is bound locally first frame of the current env. x: 7 turtle(x) turtle return x * y + 2 • nonlocal x SyntaxError: name 'x' is parameter mutant • "x" is bound locally and nonlocal def mutant(y): • nonlocal x 16 y, x = y+1, y+2 SyntaxError: no binding for nonlocal • "x" is not bound in return ninja(y)/2 mutant(y) 'x' found a non-local frame def ninja(x): y, x = y+1, y+2 return ninja(y)/2 return x + 2 Re-binds "x" to 2 in the first def turtle(x): • nonlocal x 32 return x * y + 2 non-local frame of the current • "x" is bound in a ninja(y) environment in which that name is y, ninja = 5, turtle non-local frame already bound. return x * y + 2 mutant(y) 7 8 Assignment Review: Teenage Mutant Ninja Turtles Environment Diagram of Withdraw • Bind mutant, ninja, and turtle to their respective functions make_withdraw: • Simultaneously: bind y to 5 and ninja to the turtle function make_withdraw wd: • Apply the mutant function to 5 Intrinsic function name • In the first frame, bind y to 6 and x to 7 amount: 5 balance: balance: 20 15 • Look up ninja, which is bound to the turtle function withdraw: withdraw • Look up y, which is bound to 6 withdraw (amount): make_withdraw ... • Apply the turtle function to 6 def mutant(y): • Look up x, which is bound to 6 15 y, x = y+1, y+2 in the local frame return ninja(y)/2 wd(5) • Look up y, which is bound to 5 def ninja(x): nonlocal balance in the global frame return x + 2 wd = make_withdraw(20) if amount > balance: def turtle(x): wd(5) • Return 32 return 'Insufficient funds' return x * y + 2 balance = balance - amount y, ninja = 5, turtle • Return half the result: 16 return balance mutant(y) 9 10 Calling a Withdraw Function Twice Creating Two Different Withdraw Functions make_withdraw: make_withdraw: wd: make_withdraw make_withdraw wd2: wd: balance: 12 amount: 5 balance: balance: 15 12 withdraw: withdraw: withdraw withdraw (amount): make_withdraw withdraw (amount): amount: 3 make_withdraw balance: 7 ... withdraw withdraw: withdraw (amount): 12 make_withdraw wd(5) nonlocal balance make_withdraw(7) wd = make_withdraw(20) wd = make_withdraw(20) if amount > balance: wd(5) wd(5) def withdraw(amount): return 'Insufficient funds' wd(3) wd(3) balance = balance - amount ... wd2 = make_withdraw(7) return withdraw return balance wd2(6) 11 12
Creating Two Different Withdraw Functions The Benefit of Non-Local Assignment make_withdraw: •Ability to maintain some state that is local to a function, wd: make_withdraw but evolves over successive calls to that function. wd2: •The binding for balance in the first non-local frame of the environment associated with an instance of withdraw is balance: 12 amount: 6 inaccessible to the rest of the program. withdraw: withdraw withdraw (amount): make_withdraw •An abstraction of a bank account that manages its own internal state. balance: balance: 7 1 withdraw: withdraw (amount): make_withdraw 1 John's Steven's wd2(6) Account Account nonlocal balance wd = make_withdraw(20) if amount > balance: wd(5) $10 $1,000,000 return 'Insufficient funds' wd(3) balance = balance - amount wd2 = make_withdraw(7) return balance wd2(6) 13 14 Multiple References to a Single Withdraw Function Multiple References to a Single Withdraw Function make_withdraw: make_withdraw: make_withdraw make_withdraw wd: wd: wd2: wd2: balance: 12 11 balance: 12 11 10 amount: 1 amount: 1 withdraw: withdraw: withdraw withdraw withdraw (amount): withdraw (amount): amount: 1 make_withdraw make_withdraw withdraw 11 10 wd2(1) wd(1) wd = make_withdraw(12) wd = make_withdraw(12) nonlocal balance nonlocal balance wd2 = wd wd2 = wd if amount > balance: if amount > balance: wd2(1) wd2(1) return 'Insufficient funds' return 'Insufficient funds' wd(1) wd(1) balance = balance - amount balance = balance - amount return balance return balance 15 16 Sameness and Change Referential Transparency, Lost • So long as we never modify data objects, we can regard a compound •An expression is referentially transparent if its value does object to be precisely the totality of its pieces . not change when we substitute one of its subexpression with the value of that subexpression. • A rational number is determined by its numerator and denominator. • This view is no longer valid in the presence of change . mul(add(2, mul(4, 6)), add(3, 5)) • Now, a compound data object has an "identity" that is something more than the pieces of which it is composed. mul(add(2, 24 ), add(3, 5)) • A bank account is still "the same" bank account even if we change the balance by making a withdrawal. mul( 26 , add(3, 5)) • Conversely, we could have two bank accounts that happen to have the same balance, but are different objects . •Re-binding operations violate the condition of referential transparency because they do more than return a value; they John's Steven's change the environment. Account Account •Two separately defined functions are not the same, because $10 $10 changes to one may not be reflected in the other. 17 18
Recommend
More recommend