從零開始的python學習——函數(2)

? ? ? ? ?

づ?ど

?🎉?歡迎點贊支持🎉

個人主頁:勵志不掉頭發的內向程序員;

專欄主頁:python學習專欄;


文章目錄

前言

一、變量作用域

二、函數執行過程

三、鏈式調用

四、嵌套調用

五、函數遞歸

六、參數默認值

七、關鍵字參數

總結


前言

上一章節我們了解到了 Python 中的函數的最基本的定義和調用規則,本章節我們繼續來深入函數學習,來學習函數的不同結構以及不同的調用方法。我們一起來看看吧。


一、變量作用域

來觀察以下代碼。

def getPoint():x = 10y = 20return x, yx, y = getPoint()

在這個代碼中,函數內部存在 x,y,函數外部也有 x,y。但其實這兩個 x,y 不是相同的變量,而只是恰好有一樣的名字而已。

我們可能會經常發現有的時候同一個變量名在不同的地方多次出現。但是這個同一個變量名并不意味著是同一個變量。就比如我管我最喜歡的東西叫做 “寶貝兒”,但是大家肯定也有自己的 “寶貝兒”。雖然都是 “寶貝兒”,但是顯而易見不是同一個東西。

雖然名字一樣,但其實也是不會混淆的。因為它是在各自不同的范圍生效的。我叫我的寶貝是我和我寶貝之間的事情,而其他人叫他的寶貝也只是他和他寶貝之間的事情。它們互不影響。

以上的一個情況就描述了一個概念,那就是變量的作用域。一個變量名的有效范圍是一定的,只在一個固定的區域內生效。

我們剛才的那一串代碼。

def getPoint():x = 10y = 20return x, yx, y = getPoint()

getPoint() 中的 x,y 是函數內部的變量名,函數內部的變量名只能在函數內部生效,出了函數就無效了。

def getPoint():x = 10y = 20return x, ygetPoint()
print(x, y)

我們來嘗試直接在函數外面打印我們函數內部的變量名。

它會報錯,并且說 x 未定義。既然我們函數內部的變量在函數外部無法生效,這也就意味著我們在函數外面同樣可以使用 x,y 的變量名。此時,這兩個 x,y 就是完全不相同的變量。

我們再來看看這個例子。

x = 10def test():x = 20print(f"函數內部:{x}")test()
print(f"函數外部:{x}")

此時我們就能更加清楚的看到相同變量名的區別了。

我們把 x = 10 代碼所處的位置叫做全局變量,全局變量是在整個程序中都有效的。而函數內部的 x = 20 叫做局部變量,只在函數內部有效。由于全局變量是在整個程序中都有效的,所以我們函數中也是可以使用我們的全局變量的。

x = 10def test():print(f"函數內部:{x}")test()

當函數訪問某個變量的時候,會先嘗試在局部變量中查找,如果找到,就直接訪問,如果沒找到,就會往上一級作用域中進行查找。test 再往上一級作用域,就是全局了。

可以如愿的輸出我們的 10。

我們讀取還是比較簡單的,但是我們修改操作就沒那么容易了。

x = 10
# 使用這個函數,把全局變量 x 改成20
def test():x = 20test()
print(f"x = {x}")

我們如果直接修改是不行的。

這是因為我們 x = 20 的 x 被當成函數內部創建的一個局部變量。所以沒辦法改,如果想要改成20,我們就得使用 Python 中的一個關鍵字 global 聲明一下我們的 x,我們的就能修改全局變量的 x 了。

x = 10
# 使用這個函數,把全局變量 x 改成20
def test():global xx = 20test()
print(f"x = {x}")

此時我們的修改就是針對全局的修改了。

當然,像 if,else,while,for 這些關鍵字也會引入 “代碼塊”,但是這些代碼塊不會對變量的作用域產生影響。在上述語句代碼塊內部定義的變量,可以在外面被訪問。

for i in range(1, 11):print(i)print("--------------------")
print(i)

我們可以看到,我們在 for 循環里面創建的變量 i,在外面也能打印出來。

其他的也是同理。所以說不是遇到代碼塊就一定會影響作用域,在 Python 中只要函數和類里面的代碼塊才會涉及到作用域。

二、函數執行過程

  • 調用函數才會執行函數體代碼,不調用則不會執行。
  • 函數體執行結束(或者遇到 return 語句),則回到函數調用位置,繼續往下執行。
def test():print("執行函數體代碼")print("執行函數體代碼")print("執行函數體代碼")print("111111")
test()
print("222222")
test()
print("333333")
test()
print("444444")

我們通過這個代碼來嘗試看看我們函數的執行過程。

我們可以通過執行結果來簡單分析一下。

我們程序的運行就是在這些各種函數之間不斷的跳轉,就像盜夢空間一樣,可以在夢里做夢,當夢醒了就又跳轉回原來的夢中,就在這些夢中不斷的跳轉。

我們剛才是通過函數結果來一一分析的,其實我們還可以調用 PyCharm?中的調試器,也能看到一樣的效果。我們可以在行首部分點擊一下就可以打一個斷點,再點一下就會取消。

當我們右鍵時不要選擇運行,而是選擇下面的調試。

我們的程序就會執行到調試執行的模式。我們調試執行和運行的區別就在于我們調試執行遇到斷點就會停下來,也可以隨時停下來,方便我們程序猿去觀察程序的中間過程。

此時我們的程序運行到我們斷點處就停下來了。此時我們就可以讓我們程序一步一步的走。

我們可以在這個地方看到好幾個按鈕。

我們選擇這個按鈕,它就是單步執行我們的代碼,并且遇到自己的函數,能夠進入到函數里面。當然只有調試才可以,運行不行。大家可以自己去嘗試一下,可以看到我們程序的執行過程就和剛才推測的一樣。

按鈕下面是函數調用棧,描述了當時的代碼是怎么跳轉過去的(進一步的也就是函數之間的調用關系)。

三、鏈式調用

所謂的鏈式調用,就是用一個函數的返回值作為另一個函數的參數。

我們之前寫過一個代碼。

def isOdd(num):if num % 2 == 0:return Falsereturn Trueresult = isOdd(10)
print(result)

直接運行就能打印出我們正確的值。

當然,這個代碼我們可以直接簡化成這個樣子。

def isOdd(num):if num % 2 == 0:return Falsereturn Trueprint(isOdd(10))

形如這樣的代碼,就叫做函數的鏈式調用。當然,我們的鏈式調用還可以有多層。

def isOdd(num):if num % 2 == 0:return Falsereturn Truedef add(x, y):return x + yprint(isOdd(add(10, 3)))

此時就相當于又來了一層鏈式調用,此時我們add算出來返回值為 13,我們返回值13就變成了isOdd 的參數了,然后 isOdd 判斷出來是奇數,返回值 True 又變成了 print 后輸出。

在鏈式調用中,先執行 ( ) 里面的函數,后執行外面的函數。換句話說,調用一個函數,就需要先對他的參數求值。

雖然我們鏈式嵌套是很常見的,但是我們也不用嵌套層次太深,因為這樣會影響我們的可讀性。

四、嵌套調用

一個函數的函數體內部,還可以調用其他函數。

舉一個簡單的例子。

def test():print("hello")test()

這就屬于一個嵌套調用,我們的 test 中嵌套了一個 print,雖然不是我們自己寫的,但它也是一個函數。

同時,我們嵌套調用的層次可以有很多層

def a():print("函數 a")def b():print("函數 b")a()def c():print("函數 c")b()def d():print("函數 d")c()d()

這就是我們比較復雜一點的函數嵌套。

我們來看看這個代碼的執行過程。

雖然我們函數嵌套了多層,但是函數的執行邏輯是沒有什么變化的。

如果我們把代碼的位置稍微修改一下,我們的執行結果就會天差地別。

def a():print("函數 a")def b():a()print("函數 b")def c():b()print("函數 c")def d():c()print("函數 d")d()

我們這里先調用在打印,結果完全相反。

我們代碼稍微微調了一下后就出現了截然相反的結果,所以我們一定要搞明白函數調用的基本過程。當然我們上述過程依然可以用調試器分析。

五、函數遞歸

遞歸時嵌套調用中的一種特殊情況,即一個函數嵌套調用自己。

我們大部分人可能小時候就聽過一個故事:從前有座山,山里有座廟,廟里有個老和尚再給小和尚講故事,故事的內容是:從前做山........

這個故事永遠沒有盡頭。

我們可以來試著用上面的邏輯來生成一個代碼。

# 寫一個函數,來求 n 的階乘(n 是正整數)
def factor(n):result = 1for i in range(1, n + 1):result *= ireturn resultprint(factor(5))

這是我們通過循環的方式來寫的求階乘的方法。

我們覺得沒啥意思,于是決定換一種方式,通過遞歸的方式來寫。

我們可以發現:

n! => n * ( n?- 1 )!? ? ? ? // 遞推公式

1! == 1? ? ? ? ? ? ? ? ? ? ? ? // 初始條件

我們知道了遞推公式和初始條件,我們就能來寫遞歸函數了。

def factor(n):if n == 1:return 1return n * factor(n - 1)print(factor(5))

我們 return n * factor(n - 1) 其實就是我們的遞推公式,而 return 1 就是我們的初始條件。

這一串代碼雖然寫起來沒幾行,但是其實理解起來還挺困難的,我們來看看它的執行過程。

我們可以看到,雖然都是 n,但是 n是函數形參,形參相當于函數的局部變量。我們的遞歸代碼,不會無限的往下執行,會在每一次遞歸的時候,都會無限逼近遞歸的結束條件。

我們遞歸的代碼,雖然很多時候看起來寫法很簡單,但是執行過程可能會非常復雜,在分析遞歸代碼的時候,光靠腦子想,是很困難的。所以在分析遞歸過程時,最好就是畫圖,或者借助調試器去進一步了解遞歸的過程。

我們遞歸代碼必須有兩個要素,第一就是遞歸結束條件,第二就是遞歸的遞推公式。這和數學歸納法很相像(也是兩個條件:初始條件和遞推公式)。

我們遞歸的缺點在于:

1、執行過程非常復雜,難以理解。

2、遞歸代碼容易出現棧溢出的情況。

3、遞歸代碼一般都是可以轉換成等價的循環代碼的,并且循環的版本通常運行速度比遞歸版本有優勢。

我們的遞歸代碼,不會無限的往下執行,會在每一次遞歸的時候,都會無限逼近遞歸的結束條件,但是如果我們的代碼寫錯了,就會導致我們每次遞歸參數不能正確的接近遞歸結束條件,就會出現無限遞歸的情況。

def factor(n):if n == 1:return 1return n * factor(n)print(factor(5))

假如是這樣就會報錯。

遞歸的優點就在于代碼非常簡潔,尤其是處理一些 “問題本身就是通過遞歸的方式定義的”。

六、參數默認值

Python 中的函數,可以給形參指定默認值。

帶有默認值的參數,可以在調用的時候不傳參

def add(x, y):print(f"x = {x}, y = {y}")return x + yresult = add(10, 20)
print(result)

我們完成了一個基本的函數調用。

在函數內部加上打印信息,可以方便我們進行調試。但是像這種調試信息,我們希望在正式發布的時候不要有,只是在調試階段才有,此時我們可以給我們代碼增加一個條件語句。

def add(x, y, debug):if debug:print(f"x = {x}, y = {y}")return x + yresult = add(10, 20, True)
print(result)

此時我們可以在調用函數時傳遞 True/False,當傳遞 True 時就有調試信息,傳遞 False 時就沒有。

True:

False:

但是這樣每次傳遞函數都得傳一個 True/False,看上去比較丑,所以我們可以在 debug 上指定一個默認參數。

def add(x, y, debug = False):if debug:print(f"x = {x}, y = {y}")return x + yresult = add(10, 20)
print(result)

此時如果我們不傳 debug 的參數,就默認是 False。

如果我們想要調試,只需要手動加上我們的 True即可。

result = add(10, 20, True)

我們此處的 debug = False 就是我們形參的默認值,帶有默認值的形參就可以在調用函數時不必傳參,不傳參就是使用我們的默認值。通過這樣的默認值,就可以讓我們函數設計的更加靈活。

我們默認值也有一些要求,它要求帶有默認值的形參,得在形參列表后面,而不能在中間。

def add(x, debug = False, y):if debug:print(f"x = {x}, y = {y}")return x + y

這樣是不允許的,這樣我們傳遞第二個參數時不知道是給 debug 還是給 y 的。

錯誤提示是不能把非默認參數放到默認參數前。所以如果多個帶有默認參數的形參,這些都得放到后面。

七、關鍵字參數

在調用函數的時候,需要給函數指定的實參,一般默認情況下是按照形參順序,來依次傳遞實參的。但是我們也可以通過關鍵字參數來調整這里的傳參順序,顯式指定當前實參傳遞給哪個形參。

def add(x, y):return x + yadd(10, 20)

我們的第一個實參 10 就是傳遞給我們第一個形參?x 的,而第二個實參 20 就是傳遞給我們第二個形參?y 的。這種按照先后順序來傳參的這種傳參風格稱為 “位置參數”。這是各個編程語言中最普遍的方式。

除了上種方式外,Python 還有一中非常有特色的傳參方式,那就是關鍵字傳參,也就是按照形參的名字來進行傳參。

def test(x, y):print(f'x = {x}')print(f'y = {y}')test(x = 10, y = 20)

我們調用的參數中的 x、y 就是從形參而來。此時我們運行程序。

使用這種關鍵字參數的最大意義就是能夠非常明顯的告訴我們程序猿說我們的這些參數是要傳給誰。另外可以無視形參和實參的順序。

test(y = 100, x = 200)

倒過來寫也是完全可以的。

我們位置參數和關鍵字參數還可以混合使用,只不過混合使用時我們要求位置參數在前,關鍵字參數在后。

而我們關鍵字參數一般都是搭配默認參數來使用的。一個函數,可以提供很多參數來實現對這個函數內部功能做出一些調整設定,但是為了降低調用者的使用成本,就可以把大部分參數設定出默認值。當調用者需要調整其中的一部分參數時,就可以搭配關鍵字參數來進行操作。


總結

以上便是我們函數主要的內容,比起其他的語言,多了不少新的特性,我們可以多加感悟它的新特性,但是想要熟練掌握還是得多多刷題,多多敲代碼,練習也是學習的一部分。

🎇堅持到這里已經很厲害啦,辛苦啦🎇

? ? ? ? ?

づ?ど

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95389.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95389.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95389.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RAG 的完整流程是怎么樣的?

RAG(檢索增強生成)的完整流程可分為5個核心階段:數據準備:清洗文檔、分塊處理(如PDF轉文本切片);向量化:使用嵌入模型(如BERT、BGE)將文本轉為向量&#xff1…

研發文檔版本混亂的根本原因是什么,怎么辦

研發文檔版本混亂的根本原因通常包括缺乏統一的版本控制制度、團隊協作不暢、文檔管理工具使用不當以及項目需求頻繁變化等因素。這些問題使得研發團隊在日常工作中容易出現文檔版本混亂的情況,導致信息的不一致性、溝通不暢以及開發進度的延誤。為了解決這一問題&a…

ChartView的基本使用

Qt ChartView(準確類名 QChartView)是 Qt Charts 模塊里最常用的圖表顯示控件。一句話概括:“它把 QChart 畫出來,并自帶縮放、平移、抗鋸齒等交互能力”。QML ChartView 簡介(一句話先記住:ChartView 是 Q…

系統擴展策略

1、核心指導思想:擴展立方體 在討論具體策略前,先了解著名的擴展立方體(Scale Cube),它定義了三種擴展維度: X軸:水平復制(克隆) 策略:通過負載均衡器&#…

HBuilder X 4.76 開發微信小程序集成 uview-plus

簡介 本文記錄了在HBuilder中創建并配置uni-app項目的完整流程。 首先創建項目并測試運行,確認無報錯后添加uView-Plus組件庫。 隨后修改了main.js、uni.scss、App.vue等核心文件,配置manifest.json并安裝dayjs、clipboard等依賴庫。 通過調整vite.c…

第4章:內存分析與堆轉儲

本章概述內存分析是 Java 應用性能調優的核心環節之一。本章將深入探討如何使用 VisualVM 進行內存分析,包括堆內存監控、堆轉儲生成與分析、內存泄漏檢測以及內存優化策略。通過本章的學習,你將掌握識別和解決內存相關問題的專業技能。學習目標理解 Jav…

面經分享一:分布式環境下的事務難題:理論邊界、實現路徑與選型邏輯

一、什么是分布式事務? 分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位于不同的分布式系統的不同節點之上。 一個典型的例子就是跨行轉賬: 用戶從銀行A的賬戶向銀行B的賬戶轉賬100元。 這個操作包含兩個步驟: 從A賬戶扣減100元。 向B賬戶…

C++的演化歷史

C是一門這樣的編程語言: 兼顧底層計算機硬件系統和高層應用抽象機制從實際問題出發,注重零成本抽象、性能、可移植性、與C兼容語言特性和細節很多,學習成本較高,是一門讓程序員很難敢說精通的語言 C是自由的,支持5種…

Qt6實現繪圖工具:12種繪圖工具全家桶!這個項目滿足全部2D場景

項目概述 一個基于Qt框架開發的專業繪圖工具,實現了完整的2D圖形繪制、編輯和管理功能。該項目采用模塊化設計,包含圖形繪制、圖層管理、命令模式撤銷重做、用戶界面等多個子系統,是學習現代C++和Qt框架的最佳實踐。 核心功能特性 12種專業繪圖工具 多圖層繪制系統 完整的…

Linux驅動開發學習筆記

第1章 Linux驅動開發的方式mmap映射型設計方法。【不推薦】將芯片上的物理地址映射到用戶空間的虛擬地址上,用戶操作虛擬地址來操作硬件。使用文件操作集(file_operatiopns)設計方法。【極致推薦】platfrom總線型設置方法。【比較流行】設備樹。【推薦】第2章 Linux…

mac中進行適用于IOS的靜態庫構建

前沿: 進行C開發完成之后,需要將代碼編譯成靜態庫,并且在IOS的手機系統中執行,因此記錄該實現過程. 1主要涉及內容 1.1 整體文件架構 gongyonglocalhost Attention % tree -L 2 . ├── build │ ├── __.SYMDEF │ ├── cmake_install.cmake │ ├── CMakeCache…

C++二維數組的前綴和

C二維數組的前綴和的方法很簡單&#xff0c;可以利用公式res[i][j]arr[i][j]res[i-1][j]prefix[i][j-1]-res[i-1][j-1]。輸入4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16輸出1 3 6 10 6 14 24 36 15 33 54 78 28 60 96 136#include<bits/stdc.h> using namespace std; int…

Wifi開發上層學習1:實現一個wifi搜索以及打開的app

Wifi開發上層學習1&#xff1a;實現一個wifi搜索以及打開的app 文章目錄Wifi開發上層學習1&#xff1a;實現一個wifi搜索以及打開的app背景demo實現1.添加系統權限以及系統簽名2.布局配置3.邏輯設計3.1 wifi開關的實現3.2 wifi掃描功能3.3 連接wifi總結一、WiFi 狀態控制接口二…

【DSP28335 入門教程】定時器中斷:為你的系統注入精準的“心跳”

大家好&#xff0c;歡迎來到 DSP28335 的核心精講系列。我們已經掌握了如何通過外部中斷來響應“外部事件”&#xff0c;但系統內部同樣需要一個精準的節拍器來處理“內部周期性任務”。單純依靠 DELAY_US() 這樣的軟件延時&#xff0c;不僅精度差&#xff0c;而且會在延時期間…

從零開始:用代碼解析區塊鏈的核心工作原理

區塊鏈技術被譽為信任的機器&#xff0c;它正在重塑金融、供應鏈、數字身份等眾多領域。但對于許多開發者來說&#xff0c;它仍然像一個神秘的黑盒子。今天&#xff0c;我們將拋開炒作的泡沫&#xff0c;深入技術本質&#xff0c;用大約100行Python代碼構建一個簡易的區塊鏈&am…

網絡通信IP細節

目錄 1.通信的NAT技術 2.代理服務器 3.內網穿透和內網打洞 1.通信的NAT技術 NAT技術產生的背景是我們為了解決IPV4不夠用的問題&#xff0c;NAT在通信的時候可以對IP將私網IP轉化為公網IP&#xff0c;全局IP要求唯一&#xff0c;但是私人IP不是唯一的。 將報文發給路由器進行…

國內真實的交換機、路由器和分組情況

一、未考慮擁擠情況理想狀態的網絡通信 前面我對骨干網&#xff1a; 宜春城區SDH網圖分析-CSDN博客 數據鏈路層MAC傳輸&#xff1a; 無線通信網卡底層原理&#xff08;Inter Wi-Fi AX201&#xff09;_ax201ngw是cnvio轉pci-e-CSDN博客 物理層、數據鏈路層、網絡層及傳輸層…

atomic常用類方法

Java中的java.util.concurrent.atomic包提供了多種原子操作工具類&#xff0c;以下是核心類及其方法&#xff1a;?1. AtomicBoolean??方法?&#xff1a;get()&#xff1a;獲取當前值set(boolean newValue)&#xff1a;強制設置值compareAndSet(boolean expect, boolean upd…

算法題打卡力扣第3題:無重復字符的最長子串(mid)

文章目錄題目描述解法一&#xff1a;暴力解解法二&#xff1a;滑動窗口題目描述 解法一&#xff1a;暴力解 遍歷每一個可能的子串&#xff0c;然后逐一判斷每個子串中是否有重復字符。 具體步驟&#xff1a; 使用兩層嵌套循環來生成所有子串的起止位置&#xff1a; 外層循環 i…

HTML5 簡介和基礎骨架

一、HTML5 簡介HTML5 是 HTML&#xff08;超文本標記語言&#xff09;的第五個主要版本&#xff0c;于 2014 年 10 月由 W3C&#xff08;萬維網聯盟&#xff09;正式發布。它不僅是對 HTML4.01 和 XHTML 的升級&#xff0c;更是一套完整的 Web 技術標準&#xff0c;包含了新的標…