引數的傳遞是透過自動將物件賦值給本地變數名來實現的。在函式執行時,函式頭部的引數名是一個新的、本地的變數名,這個變數名是在函式的本地作用域記憶體在。引數的傳遞本質上就是python賦值的另一個例項而已。
在原處改變函式的可變物件引數的值會對呼叫者有影響。函式能夠就地改變傳入的可變物件,因此其結果會影響呼叫者,這其實和前面介紹過的物件賦值原理是一樣的;
而不可變物件的引用重新賦值會指向新的物件,因此不會改變呼叫者。
先看一個不可變物件的例子
在函式中修改a對於呼叫函式的地方沒有任何影響,因為他在函式內部直接把本地變數a重置為了一個完全不同的新物件,所以他不會影響最初的變數b
而當引數傳遞像列表和字典這樣的可修改物件的時候,我們還需要注意,對這樣的可變物件的原處修改可能在函式退出後依然有效,並由此影響到呼叫者。
再次對比可以看出,呼叫者的不可變變數x沒有受到影響,而可變變數L在函式內部進行本地修改,並影響到了自身
可以看出,對於引數a,僅僅把本地變數a修改為引用一個完全不同的物件,並沒有改變呼叫者作用域中的名稱x的繫結。而引數b被傳給了一個可變物件(在呼叫者作用域中叫做L的列表),因為第二個賦值是一個在原處發生的物件改變,對函式中b[0]進行賦值的結果會在函式返回後影響L的值,他修改了b所引用的物件的一部分,因為引用共享物件的緣故,L也被同時改變。
再強調一次,其實引數傳遞後的本地修改過程和簡單物件賦值後的物件修改,實質上是一回事,換句話說就等於下面這個例子所描述的程式過程
那如何避免對可變引數的修改呢?
實際上,可變引數的原處修改行為並不是一個bug,它只是引數傳遞在python中工作的方式。在python中,預設透過引用進行函式的引數傳遞,是因為這通常是我們所想要的:這意味著不需要建立多個複製就可以在我們的程式中傳遞很大的物件。如果不想要函式內部在原處的修改影響傳遞給它的物件,那麼,我們可以簡單的建立一個明確的可變物件的複製
或者在函式內部進行複製,這樣可以不改變傳入的物件,函式呼叫看上去沒有變化
引數的傳遞是透過自動將物件賦值給本地變數名來實現的。在函式執行時,函式頭部的引數名是一個新的、本地的變數名,這個變數名是在函式的本地作用域記憶體在。引數的傳遞本質上就是python賦值的另一個例項而已。
在原處改變函式的可變物件引數的值會對呼叫者有影響。函式能夠就地改變傳入的可變物件,因此其結果會影響呼叫者,這其實和前面介紹過的物件賦值原理是一樣的;
而不可變物件的引用重新賦值會指向新的物件,因此不會改變呼叫者。
先看一個不可變物件的例子
在函式中修改a對於呼叫函式的地方沒有任何影響,因為他在函式內部直接把本地變數a重置為了一個完全不同的新物件,所以他不會影響最初的變數b
而當引數傳遞像列表和字典這樣的可修改物件的時候,我們還需要注意,對這樣的可變物件的原處修改可能在函式退出後依然有效,並由此影響到呼叫者。
再次對比可以看出,呼叫者的不可變變數x沒有受到影響,而可變變數L在函式內部進行本地修改,並影響到了自身
可以看出,對於引數a,僅僅把本地變數a修改為引用一個完全不同的物件,並沒有改變呼叫者作用域中的名稱x的繫結。而引數b被傳給了一個可變物件(在呼叫者作用域中叫做L的列表),因為第二個賦值是一個在原處發生的物件改變,對函式中b[0]進行賦值的結果會在函式返回後影響L的值,他修改了b所引用的物件的一部分,因為引用共享物件的緣故,L也被同時改變。
再強調一次,其實引數傳遞後的本地修改過程和簡單物件賦值後的物件修改,實質上是一回事,換句話說就等於下面這個例子所描述的程式過程
那如何避免對可變引數的修改呢?
實際上,可變引數的原處修改行為並不是一個bug,它只是引數傳遞在python中工作的方式。在python中,預設透過引用進行函式的引數傳遞,是因為這通常是我們所想要的:這意味著不需要建立多個複製就可以在我們的程式中傳遞很大的物件。如果不想要函式內部在原處的修改影響傳遞給它的物件,那麼,我們可以簡單的建立一個明確的可變物件的複製
或者在函式內部進行複製,這樣可以不改變傳入的物件,函式呼叫看上去沒有變化
醬油哥:來吧,一起踏上Python資料科學之路