一个问题
使用scheme定义的银行账户模拟函数,可以十分完美的保持和更新函数内的balance状态
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispath m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
dispatch)
>> (define acc (make-account 100))
>> ((acc 'withdraw) 50)
50
>> ((acc 'deposit) 60)
110
>> ((acc 'withdraw) 120)
"Insufficient funds"
使用Python定义的同样的函数
def make_account(balance):
def withdraw(amount):
if balance > amount:
balance = balance - amount
return balance
else:
print("Insufficient funds")
def deposit(amount):
balance = balance + amount
return balance
def dispath(msg):
if msg == "withdraw":
return withdraw
elif msg == "deposit":
return deposit
else:
print("Unknown request -- MAKE-ACCOUNT %s" % msg)
return dispath
In [1]: from make_account1 import make_account
In [2]: acc = make_account(100)
In [3]: acc
Out[3]: <function make_account1.make_account.<locals>.dispath>
In [4]: acc("withdraw")
Out[4]: <function make_account1.make_account.<locals>.withdraw>
In [5]: acc("withdraw")(10)
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-5-0156b22a66b4> in <module>()
----> 1 acc("withdraw")(10)
E:\make_account1.py in withdraw(amount)
3 def make_account(balance):
4 def withdraw(amount):
----> 5 if balance > amount:
6 balance = balance - amount
7 return balance
UnboundLocalError: local variable 'balance' referenced before assignment
错误分析
根据错误提示,balance
在赋值前被引用,但根据词法作用域,balance
在函数withdraw
内是可以访问到make_account
的参数balance
的。问题出在Python的变量声明,如果出现value = ...
,Python就视为声明变量value
,所以balance = balance - amount
相当于JavaScript代码let balance; balance = balance - amount
,balance
在这里变成一个未被赋值的local变量,所以会出现错误”UnboundLocalError: local variable ‘balance’ referenced before assignment”。
使用Python不容易看出错误,可以用JavaScript写出相同的错误代码:
let make_account = function(balance){
let withdraw = function(amount){
let balance;
if(balance > amount){
balance = balance - amount;
return balance;
}else{
console.log("Insufficient funds");
}
};
let deposit = function(amount){
let balance;
balance = balance + amount;
return balance;
};
let dispath = function(msg){
if(msg === "withdraw"){
return withdraw;
}else if(msg === "deposit"){
return deposit;
}else{
console.log("Unknown request -- MAKE-ACCOUNT", msg);
}
};
return dispath;
};
而正确的JavaScript代码应该为:
let make_account = function(balance){
let withdraw = function(amount){
if(balance > amount){
balance = balance - amount;
return balance;
}else{
console.log("Insufficient funds");
}
};
let deposit = function(amount){
balance = balance + amount;
return balance;
};
let dispath = function(msg){
if(msg === "withdraw"){
return withdraw;
}else if(msg === "deposit"){
return deposit;
}else{
console.log("Unknown request -- MAKE-ACCOUNT", msg);
}
};
return dispath;
};
修改
既然Python将withdraw
中的balance
视为local变量,我们只需声明其为nonlocal
变量即可。之后balance
就可在enclose变量域内找到。
def make_account(balance):
def withdraw(amount):
nonlocal balance
if balance > amount:
balance = balance - amount
else:
print("Insufficient funds")
def deposit(amount):
nonlocal balance
balance = balance + amount
def dispath(msg):
if msg == "withdraw":
return withdraw
elif msg == "deposit":
return deposit
else:
print("Unknown request -- MAKE-ACCOUNT %s" % msg)
return dispath
In [1]: from make_account2 import make_account
In [2]: acc = make_account(100)
In [3]: acc("withdraw")(10)
Out[3]: 90
In [4]: acc("withdraw")(100)
Insufficient funds
In [5]: acc("deposit")(50)
Out[5]: 140
- older
- Newer