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

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

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

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

-Advertisement-
Play Games
更多相關文章
  • 先定義消息類型 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組 ...
  • 前言 在日常的工作與學習中,我們經常會使用思維導圖這個工具,來把抽象而又無形的思考轉換成有形並且具體的圖像,是理清思路,梳理邏輯的一大神器。 準確的說,思維導圖並不是一個具體的工具,而是一種方法。是道而不是術。實際落地時,我們通常還需要藉助外部的工具。從最原始的紙和筆,到各種軟體,可以說是應用盡有。 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...