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組 ...
  • 前言 在日常的工作與學習中,我們經常會使用思維導圖這個工具,來把抽象而又無形的思考轉換成有形並且具體的圖像,是理清思路,梳理邏輯的一大神器。 準確的說,思維導圖並不是一個具體的工具,而是一種方法。是道而不是術。實際落地時,我們通常還需要藉助外部的工具。從最原始的紙和筆,到各種軟體,可以說是應用盡有。 ...
一周排行
  • 一、引言 按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程式才可以調用的隊列,有些情況下為了安全起見定義為私有隊列。所以剛開始的時候認為,要想訪問遠程消息隊列,只能使用公共隊列。但是後來發現,公共隊列依賴Domain Controller(域控),在 ...
  • 本文只對api介面,header請求參數進行簡單驗證,起到拋磚引玉使用,需要深入驗證,請自行擴展 項目目錄結構如圖 中間件類 using ApiMiddleware.Common.DataEnityModel; using ApiMiddleware.Common.DbContext; using ...
  • 前言:由於公司占時沒有運維,出於微服務的需要,Apollo只能先裝在windows 阿裡雲上跑起來,由於環境及網路等問題,在安裝過程中遇到很多坑,算是一個個坑填完後,最終實現。 一. java jdk環境 java jdk 1.8下載地址: https://www.oracle.com/java/t ...
  • 前言 nuget 是 .net 的常用包管理器,目前已經內置到 Visual Studio 2012 以後的版本。大多數 .net 包都托管在 nuget.org,包括 .net core 框架基礎包,得益於 .net core 的模塊化設計,很多非核心包都可以進行一定程度的獨立升級。 製作並上傳 ...
  • 簡單的介紹一下集合,通俗來講就是用來保管多個數據的方案。比如說我們是一個公司的倉庫管理,公司有一堆貨物需要管理,有同類的,有不同類的,總而言之就是很多、很亂。我們對照集合的概念對倉庫進行管理的話,那麼 數組就是將一堆貨整整齊齊的碼在倉庫的某個地方,普通列表也是如此;Set就是在倉庫里有這麼一個貨架, ...
  • 中間件分類 ASP.NET Core 中間件的配置方法可以分為以上三種,對應的Helper方法分別是:Run(), Use(), Map()。 Run(),使用Run調用中間件的時候,會直接返回一個響應,所以後續的中間件將不會被執行了。 Use(),它會對請求做一些工作或處理,例如添加一些請求的上下 ...
  • 字元串的常用操作 很好理解 字元串可以用 ' + ' 連接,或者乘一個常數重覆輸出字元串 字元串的索引操作 通過一對中括弧可以找到字元串中的某個字元 可以通過正負數雙向操作噢 用一個中括弧來實現 為什麼沒有-0??去清醒腦子想想 -0 和 0 有差嗎? 還有一個切片操作 就像切菜那樣簡單,同樣是中括 ...
  • title: Java基礎語法(3) 運算符 blog: "CSDN" data: "Java學習路線及視頻" 1.算術運算符 算術運算符的註意問題 如果對負數取模,可以把模數負號忽略不記,如:5% 2=1。 但被模數是負數則不可忽略。此外,取模運算的結果不一定總是整數。 對於除號“/”,它的整數除 ...
  • 下麵是互相轉換的代碼: 有想要瞭解更多關於python知識的請在下方評論或私信小編 ...
  • 引言 構建分散式系統並不容易。然而,人們日常所使用的應用大多基於分散式系統,在短時間內依賴於分散式系統的現狀並不會改變。ApacheZooKeeper旨在減輕構建健壯的分散式系統的任務。ZooKeeper基於 分散式計算的核心概念而設計,主要目的是給開發人員提供一套容易理解和開發的介面,從而簡化分佈 ...
x