# 函數進階
## 目標
* 函數參數和返回值的作用
* 函數的返回值 進階
* 函數的參數 進階
* 遞歸函數
## 01. 函數參數和返回值的作用
函數根據 **有沒有參數** 以及 **有沒有返回值**,可以 **相互組合**,一共有 **4 種** 組合形式
1. 無參數,無返回值
2. 無參數,有返回值
3. 有參數,無返回值
4. 有參數,有返回值

> 定義函數時,**是否接收參數,或者是否返回結果**,是根據 **實際的功能需求** 來決定的!
1. 如果函數 **內部處理的數據不確定**,就可以將外界的數據以參數傳遞到函數內部
2. 如果希望一個函數 **執行完成后,向外界匯報執行結果**,就可以增加函數的返回值
### 1.1 無參數,無返回值
此類函數,不接收參數,也沒有返回值,應用場景如下:
1. **只是單純地做一件事情**,例如 **顯示菜單**
2. 在函數內部 **針對全局變量進行操作**,例如:**新建名片**,最終結果 **記錄在全局變量** 中
> 注意:
* 如果全局變量的數據類型是一個 **可變類型**,在函數內部可以使用 **方法** 修改全局變量的內容 —— **變量的引用不會改變**
* 在函數內部,**使用賦值語句** 才會 **修改變量的引用**
### 1.2 無參數,有返回值
此類函數,不接收參數,但是有返回值,應用場景如下:
* 采集數據,例如 **溫度計**,返回結果就是當前的溫度,而不需要傳遞任何的參數
### 1.3 有參數,無返回值
此類函數,接收參數,沒有返回值,應用場景如下:
* 函數內部的代碼保持不變,針對 **不同的參數 處理 不同的數據**
* 例如 **名片管理系統** 針對 **找到的名片** 做 **修改**、**刪除** 操作
### 1.4 有參數,有返回值
此類函數,接收參數,同時有返回值,應用場景如下:
* 函數內部的代碼保持不變,針對 **不同的參數 處理 不同的數據**,并且 **返回期望的處理結果**
* 例如 **名片管理系統** 使用 **字典默認值** 和 **提示信息** 提示用戶輸入內容
* 如果輸入,返回輸入內容
* 如果沒有輸入,返回字典默認值
## 02. 函數的返回值 進階
* 在程序開發中,有時候,會希望 **一個函數執行結束后,告訴調用者一個結果**,以便調用者針對具體的結果做后續的處理
* **返回值** 是函數 **完成工作**后,**最后** 給調用者的 **一個結果**
* 在函數中使用 `return` 關鍵字可以返回結果
* 調用函數一方,可以 **使用變量** 來 **接收** 函數的返回結果
> 問題:一個函數執行后能否返回多個結果?
### 示例 —— 溫度和濕度測量
* 假設要開發一個函數能夠同時返回當前的溫度和濕度
* **先完成返回溫度**的功能如下:
```python
def measure():
"""返回當前的溫度"""
print("開始測量...")
temp = 39
print("測量結束...")
return temp
result = measure()
print(result)
```
* 在利用 **元組** 在返回溫度的同時,也能夠返回 **濕度**
* 改造如下:
```python
def measure():
"""返回當前的溫度"""
print("開始測量...")
temp = 39
wetness = 10
print("測量結束...")
return (temp, wetness)
```
> 提示:如果一個函數返回的是元組,括號可以省略
**技巧**
* 在 `Python` 中,可以 **將一個元組** 使用 **賦值語句** 同時賦值給 **多個變量**
* 注意:變量的數量需要和元組中的元素數量保持一致
```python
result = temp, wetness = measure()
```
### 面試題 —— 交換兩個數字
**題目要求**
1. 有兩個整數變量 `a = 6`, `b = 100`
2. 不使用其他變量,**交換兩個變量的值**
#### 解法 1 —— 使用其他變量
```python
# 解法 1 - 使用臨時變量
c = b
b = a
a = c
```
#### 解法 2 —— 不使用臨時變量
```python
# 解法 2 - 不使用臨時變量
a = a + b
b = a - b
a = a - b
```
#### 解法 3 —— Python 專有,利用元組
```python
a, b = b, a
```
## 03. 函數的參數 進階
### 3.1. 不可變和可變的參數
> 問題 1:在函數內部,針對參數使用 **賦值語句**,會不會影響調用函數時傳遞的 **實參變量**? —— 不會!
* 無論傳遞的參數是 **可變** 還是 **不可變**
* 只要 **針對參數** 使用 **賦值語句**,會在 **函數內部** 修改 **局部變量的引用**,**不會影響到 外部變量的引用**
```python
def demo(num, num_list):
print("函數內部")
# 賦值語句
num = 200
num_list = [1, 2, 3]
print(num)
print(num_list)
print("函數代碼完成")
gl_num = 99
gl_list = [4, 5, 6]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)
```
> 問題 2:如果傳遞的參數是 **可變類型**,在函數內部,使用 **方法** 修改了數據的內容,**同樣會影響到外部的數據**
```python
def mutable(num_list):
# num_list = [1, 2, 3]
num_list.extend([1, 2, 3])
print(num_list)
gl_list = [6, 7, 8]
mutable(gl_list)
print(gl_list)
```
#### 面試題 —— `+=`
* 在 `python` 中,列表變量調用 `+=` 本質上是在執行列表變量的 `extend` 方法,不會修改變量的引用
```python
def demo(num, num_list):
print("函數內部代碼")
# num = num + num
num += num
# num_list.extend(num_list) 由于是調用方法,所以不會修改變量的引用
# 函數執行結束后,外部數據同樣會發生變化
num_list += num_list
print(num)
print(num_list)
print("函數代碼完成")
gl_num = 9
gl_list = [1, 2, 3]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)
```
### 3.2 缺省參數
* 定義函數時,可以給 **某個參數** 指定一個**默認值**,具有默認值的參數就叫做 **缺省參數**
* 調用函數時,如果沒有傳入 **缺省參數** 的值,則在函數內部使用定義函數時指定的 **參數默認值**
* 函數的缺省參數,**將常見的值設置為參數的缺省值**,從而 **簡化函數的調用**
* 例如:對列表排序的方法
```python
gl_num_list = [6, 3, 9]
# 默認就是升序排序,因為這種應用需求更多
gl_num_list.sort()
print(gl_num_list)
# 只有當需要降序排序時,才需要傳遞 `reverse` 參數
gl_num_list.sort(reverse=True)
print(gl_num_list)
```
#### 指定函數的缺省參數
* 在參數后使用賦值語句,可以指定參數的缺省值
```python
def print_info(name, gender=True):
gender_text = "男生"
if not gender:
gender_text = "女生"
print("%s 是 %s" % (name, gender_text))
```
**提示**
1. 缺省參數,需要使用 **最常見的值** 作為默認值!
2. 如果一個參數的值 **不能確定**,則不應該設置默認值,具體的數值在調用函數時,由外界傳遞!
#### 缺省參數的注意事項
##### 1) 缺省參數的定義位置
* **必須保證** **帶有默認值的缺省參數** **在參數列表末尾**
* 所以,以下定義是錯誤的!
```python
def print_info(name, gender=True, title):
```
##### 2) 調用帶有多個缺省參數的函數
* 在 **調用函數時**,如果有 **多個缺省參數**,**需要指定參數名**,這樣解釋器才能夠知道參數的對應關系!
```python
def print_info(name, title="", gender=True):
"""
:param title: 職位
:param name: 班上同學的姓名
:param gender: True 男生 False 女生
"""
gender_text = "男生"
if not gender:
gender_text = "女生"
print("%s%s 是 %s" % (title, name, gender_text))
# 提示:在指定缺省參數的默認值時,應該使用最常見的值作為默認值!
print_info("小明")
print_info("老王", title="班長")
print_info("小美", gender=False)
```
### 3.3 多值參數(知道)
#### 定義支持多值參數的函數
* 有時可能需要 **一個函數** 能夠處理的參數 **個數** 是不確定的,這個時候,就可以使用 **多值參數**
* `python` 中有 **兩種** 多值參數:
* 參數名前增加 **一個** `*` 可以接收 **元組**
* 參數名前增加 **兩個** `*` 可以接收 **字典**
* 一般在給多值參數命名時,**習慣**使用以下兩個名字
* `*args` —— 存放 **元組** 參數,前面有一個 `*`
* `**kwargs` —— 存放 **字典** 參數,前面有兩個 `*`
* `args` 是 `arguments` 的縮寫,有變量的含義
* `kw` 是 `keyword` 的縮寫,`kwargs` 可以記憶 **鍵值對參數**
```python
def demo(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)
```
> 提示:**多值參數** 的應用會經常出現在網絡上一些大牛開發的框架中,知道多值參數,**有利于我們能夠讀懂大牛的代碼**
#### 多值參數案例 —— 計算任意多個數字的和
**需求**
1. 定義一個函數 `sum_numbers`,可以接收的 **任意多個整數**
2. 功能要求:將傳遞的 **所有數字累加** 并且返回累加結果
```python
def sum_numbers(*args):
num = 0
# 遍歷 args 元組順序求和
for n in args:
num += n
return num
print(sum_numbers(1, 2, 3))
```
#### 元組和字典的拆包(知道)
* 在調用帶有多值參數的函數時,如果希望:
* 將一個 **元組變量**,直接傳遞給 `args`
* 將一個 **字典變量**,直接傳遞給 `kwargs`
* 就可以使用 **拆包**,簡化參數的傳遞,**拆包** 的方式是:
* 在 **元組變量前**,增加 **一個** `*`
* 在 **字典變量前**,增加 **兩個** `*`
```python
def demo(*args, **kwargs):
print(args)
print(kwargs)
# 需要將一個元組變量/字典變量傳遞給函數對應的參數
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}
# 會把 num_tuple 和 xiaoming 作為元組傳遞個 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)
```
## 04. 函數的遞歸
> 函數調用自身的 **編程技巧** 稱為遞歸
### 4.1 遞歸函數的特點
**特點**
* **一個函數** **內部** **調用自己**
* 函數內部可以調用其他函數,當然在函數內部也可以調用自己
**代碼特點**
1. 函數內部的 **代碼** 是相同的,只是針對 **參數** 不同,**處理的結果不同**
2. 當 **參數滿足一個條件** 時,函數不再執行
* **這個非常重要**,通常被稱為遞歸的出口,否則 **會出現死循環**!
示例代碼
```python
def sum_numbers(num):
print(num)
# 遞歸的出口很重要,否則會出現死循環
if num == 1:
return
sum_numbers(num - 1)
sum_numbers(3)
```

### 4.2 遞歸案例 —— 計算數字累加
**需求**
1. 定義一個函數 `sum_numbers`
2. 能夠接收一個 `num` 的整數參數
3. 計算 1 + 2 + ... num 的結果
```python
def sum_numbers(num):
if num == 1:
return 1
# 假設 sum_numbers 能夠完成 num - 1 的累加
temp = sum_numbers(num - 1)
# 函數內部的核心算法就是 兩個數字的相加
return num + temp
print(sum_numbers(2))
```

> 提示:遞歸是一個 **編程技巧**,初次接觸遞歸會感覺有些吃力!在處理 **不確定的循環條件時**,格外的有用,例如:**遍歷整個文件目錄的結構**
- linux基礎
- 01_Python基礎課程安排
- 02_操作系統(科普章節)
- 03_操作系統的發展史(科普章節)
- 04_文件和目錄(理解)
- 05_Ubuntu圖形界面入門
- 06_常用Linux命令的基本使用
- 07_Linux終端命令格式
- 08_文件和目錄常用命令
- 09_遠程管理常用命令
- 10_用戶權限相關命令
- 11_系統信息相關命令
- 12_其他命令
- python基礎
- 01_認識 Python
- 02_第一個Python 程序
- 03_PyCharm的初始設置(知道)
- 04_多文件項目的演練
- 05_注釋
- 06_算數運算符
- 07_程序執行原理(科普)
- 08_變量的基本使用
- 09_變量的命名
- 10_判斷(if)語句
- 11_運算符
- 12_循環
- 13_函數基礎
- 14_高級變量類型
- 15_綜合應用——名片管理系統
- 16_變量進階(理解)
- 17_函數進階.md
- 面向對象
- 01_面向對象(OOP)基本概念
- 02_類和對象
- 03_面向對象基礎語法
- 04_面向對象封裝案例
- 05_面向對象封裝案例 II