從上面代碼中可以看出,函數的列印的是同一個列表對象numbers,因為他們的id值是一樣的,只不過是列表中的元素在變化。為什麼會這樣呢? 這要從函數的特性說起,在 Python 中,函數是第一類對象(function is the first class object),換而言之,函數也是對象,跟整 ...
def func(numbers = [], num=1):
numbers.append(num)
for number in numbers:
print(number)
func()
>>> 1
func()
>>> 1
>>> 1
func()
>>> 1
>>> 1
>>> 1
從上面代碼中可以看出,函數的列印的是同一個列表對象numbers,因為他們的id值是一樣的,只不過是列表中的元素在變化。為什麼會這樣呢?
這要從函數的特性說起,在 Python 中,函數是第一類對象(function is the first class object),換而言之,函數也是對象,跟整數、字元串一樣可以賦值給變數、當做參數傳遞、還可以作為返回值。函數也有自己的屬性,比如函數的名字、函數的預設參數列表。
func.__name__ //函數的名字
>>> 'func'
func.__defaults__ //函數的預設參數
>>> ([1,1,1],1)
def 是一條可執行語句,Python 解釋器執行 def 語句時,就會在記憶體中就創建了一個函數對象(此時,函數裡面的代碼邏輯並不會執行,因為還沒調用嘛),在全局命名空間,有一個函數名(變數叫 func)會指向該函數對象,記住,至始至終,不管該函數調用多少次,函數對象只有一個,就是function object,不會因為調用多次而出現多個函數對象。
函數對象生成之後,它的屬性:名字和預設參數列表都將初始化完成。
初始化完成時,屬性 __ default__ 中的第一個預設參數 numbers 指向一個空列表。
當函數第一次被調用時,就是第一次執行 func()時,開始執行函數裡面的邏輯代碼(此時函數不再需要初始化了),代碼邏輯就是往
numbers中添加一個值為1的元素
第二次調用 func(),繼續往numbers中添加一個元素
第三次、四次依此類推。
PS:遇到問題沒人解答?需要Python學習資料?可以加點擊下方鏈接自行獲取
note.youdao.com/noteshare?id=2dce86d0c2588ae7c0a88bee34324d76
如果我們顯示地指定 numbers 參數,結果截然不同。
func(numbers = [10,11])
因為numbers被重新賦值了,它不再指向原來初始化時的那個列表了,而是指向了我們傳遞過去的那個新列表對象,因此返回值變成了 [10, 11, 1]
怎樣避免這種情況?
def func(numbers=None, num=1):
if not numbers:
numbers = []
numbers.append(num)
for number in numbers:
print(number)
func()
>>> 1
func()
>>> 1
func()
>>> 1
如果調用時沒有指定參數,那麼調用方法時,預設參數 numbers 每次都被重新賦值了,所以,每次調用的時候numbers都將指向一個新的對象。這就是與前者的區別所在。
那麼,是不是說我們永遠都不應該用可變對象來作為參數的預設值了嗎?並不是,既然Python有這樣的語法,就一定有他的應用場景,就像 for ... else 語法一樣。我們可以用可變對象來做緩存功能。
例如:計算一個數的階乘時可以用一個可變對象的字典當作緩存值來實現緩存,緩存中保存計算好的值,第二次調用的時候就無需重覆計算,直接從緩存中拿。
def factorial(n,cache={}):
if n ==0:
return 1
if n not in cache:
print('xxx')
cache[n] = factorial(n-1)*n
return cache[n]
>>> factorial(5)
xxx
xxx
xxx
xxx
xxx
>>> factorial(4)
第二次調用的時候,直接從 cache 中拿了值,所以,你說用可變對象作為預設值是 Python 的缺陷嗎?也並不是,對吧!你還是當作一種特性來使用
當然在JS中這種效果可以用閉包實現
function outer(){
var cache = {};
return function(n){
if(n===0)
return 1;
if(cache[n]){
console.log('xxx');
cache[n] = arguments.callee(n-1)*n;
}
return cache[n];
}
}
var factorial = outer();