Python中為什麼不能用可變對象作為預設參數的值

来源:https://www.cnblogs.com/python960410445/archive/2019/12/03/11979585.html

從上面代碼中可以看出,函數的列印的是同一個列表對象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();

您的分享是我們最大的動力!

更多相關文章
  • 先定義消息類型 orders.proto 在GOPATH創建目錄和編譯這個消息類型輸出到該目錄,包名是message 編寫go文件進行序列化和反序列化剛纔生成的包里的類型結構體數據 ...
  • swap(a,b) 用於交換a,b兩個變數的值; max(a,b) 返回a,b中的最大值; min(a,b) 返回a,b中的最小值; abs(x) 返回x的絕對值,x必須是整數; ...
  • ——日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇。 簡介 上一章我們一起學習了Java NIO的核心組件Channel,它可以看作是實體與實體之間的連接,而且需要與Buffer交互,這一章我們就來學習一下Buffer的特性。 概念 Buffer用於與Channel交互時使用,通 ...
  • 前言 "上文" 我們介紹了JDK中的線程池框架 。我們知道,只要需要創建線程的情況下,即使是在單線程模式下,我們也要儘量使用 。即: 但是,在 "《阿裡巴巴Java開發手冊》" 中有一條 【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這 ...
  • PyCharm 是一種 Python IDE,可以幫助程式員節約時間,提高生產效率。那麼具體如何使用呢?本文從 PyCharm 安裝到插件、外部工具、專業版功能等進行了一一介紹,希望能夠幫助到大家。 機器之心之前也沒系統地介紹過 PyCharm,怎樣配置環境、怎樣 DeBug、怎樣同步 GitHub ...
  • 前言本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:醍醐三葉 關於python的存儲問題, (1)由於python中萬物皆對象,所以python的存儲問題是對象的存儲問題,並且對於每個對象,python會分配一塊記憶體空間去 ...
  • [TOC] forms組件 先拋出一個需求: 手動書寫需求 views.py 這裡實現了三個功能: 手寫html頁面獲取用戶輸入信息 將數據傳入後端做數據校驗 如果有錯誤,展示錯誤信息 但是這個頁面手寫麻煩,輸入信息寫錯了,一刷新信息全沒了,很不友好!! 使用forms組件校驗數據 使用forms組 ...
  • 前言 在日常的工作與學習中,我們經常會使用思維導圖這個工具,來把抽象而又無形的思考轉換成有形並且具體的圖像,是理清思路,梳理邏輯的一大神器。 準確的說,思維導圖並不是一個具體的工具,而是一種方法。是道而不是術。實際落地時,我們通常還需要藉助外部的工具。從最原始的紙和筆,到各種軟體,可以說是應用盡有。 ...
一周排行
  • 本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的使用事件聚合器實現模塊間的通信 一.事件聚合器 在上一篇 ".NET Core 3 WPF MVVM框架 Prism系列之模塊化" 我們留下了一些問題,就是如何處理同模塊不同窗體之間的通信和不同模塊之間不同窗體的通信,Prism提 ...
  • Microsoft Visual Studio 2010 的項目為件改為Microsoft Visual Studio 2015預設打開 2010 的Solution (.Sln) file 更改為 2015 的Solution (.Sln) file ...
  • 設計一個簡單的登錄視窗,要求輸入用戶名:小金,密碼:123456時候點登錄能正確轉到另一個視窗。 1、建立窗體應用。 2、這裡創建一個login和一個NewForm的窗體。 3、在login的窗體拖拉2個label和2個textbox和1個linklabel的控制項。一個標簽名字為用戶名,一個標簽為密 ...
  • 已有站點:HTTP80 %systemroot%\system32\inetsrv\APPCMD ADD APP /SITE.NAME:"HTTP80" /path:/Redirect /physicalPath:"C:\Windows\System32\drivers\etc" /applicat ...
  • ``` var xmlstr = @" some_appid 1413192605 component_verify_ticket some_verify_ticket "; Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,記錄一下學習過程以備後續查用。 一、引言 很多人說原型設計模式會節省機器記憶體,他們說是拷貝出來的對象是原型的複製,不會使用記憶體。我認為這是不對的,因為拷貝出來的每一個對象都是實際 存在的 ...
  • 實現把String字元串轉化為In後可用參數代碼: public string StringToList(string aa) { string bb1 = "("; if (!string.IsNullOrEmpty(aa.Trim())) { string[] bb = aa.Split(new ...
  • 1.ImportData主方法 把傳入為object數組類型,按照下標取出對應的參數,此處為Table和Username public object[] ImportData(object[] Param) { DataTable dt = (DataTable)Param[0]; string m ...
  • C#實現對Excel操作,根據數據的類型不同或者來源不同會放在不同的頁簽中,C#實現添加頁簽代碼如下:(path為文檔保存的地址,dt為要處理的源數據) public void addSheet(string Path, DataTable dt) { var SlDoc = new SLDocum ...
  • public partial class Form1 : Form { public Form1() { InitializeComponent(); Dog dog = new Dog(); InsertDog(dog); dog.OnAlert(); //Console.WriteLine(); ...
x