# 函數基礎
## 目標
* 函數的快速體驗
* 函數的基本使用
* 函數的參數
* 函數的返回值
* 函數的嵌套調用
* 在模塊中定義函數
## 01. 函數的快速體驗
### 1.1 快速體驗
* 所謂**函數**,就是把 **具有獨立功能的代碼塊** 組織為一個小模塊,在需要的時候 **調用**
* 函數的使用包含兩個步驟:
1. 定義函數 —— **封裝** 獨立的功能
2. 調用函數 —— 享受 **封裝** 的成果
* **函數的作用**,在開發程序時,使用函數可以提高編寫的效率以及代碼的 **重用**
**演練步驟**
1. 新建 `04_函數` 項目
2. 復制之前完成的 **乘法表** 文件
3. 修改文件,增加函數定義 `multiple_table():`
4. 新建另外一個文件,使用 `import` 導入并且調用函數
## 02. 函數基本使用
### 2.1 函數的定義
定義函數的格式如下:
```python
def 函數名():
函數封裝的代碼
……
```
1. `def` 是英文 `define` 的縮寫
2. **函數名稱** 應該能夠表達 **函數封裝代碼** 的功能,方便后續的調用
3. **函數名稱** 的命名應該 **符合** **標識符的命名規則**
* 可以由 **字母**、**下劃線** 和 **數字** 組成
* **不能以數字開頭**
* **不能與關鍵字重名**
### 2.2 函數調用
調用函數很簡單的,通過 `函數名()` 即可完成對函數的調用
### 2.3 第一個函數演練
**需求**
* 1. 編寫一個打招呼 `say_hello` 的函數,封裝三行打招呼的代碼
* 2. 在函數下方調用打招呼的代碼
```python
name = "小明"
# 解釋器知道這里定義了一個函數
def say_hello():
print("hello 1")
print("hello 2")
print("hello 3")
print(name)
# 只有在調用函數時,之前定義的函數才會被執行
# 函數執行完成之后,會重新回到之前的程序中,繼續執行后續的代碼
say_hello()
print(name)
```
> 用 **單步執行 F8 和 F7** 觀察以下代碼的執行過程
* 定義好函數之后,只表示這個函數封裝了一段代碼而已
* 如果不主動調用函數,函數是不會主動執行的
#### 思考
* 能否將 **函數調用** 放在 **函數定義** 的上方?
* 不能!
* 因為在 **使用函數名** 調用函數之前,必須要保證 `Python` 已經知道函數的存在
* 否則控制臺會提示 `NameError: name 'say_hello' is not defined` (**名稱錯誤:say_hello 這個名字沒有被定義**)
### 2.4 PyCharm 的調試工具
* **F8 Step Over** 可以單步執行代碼,會把函數調用看作是一行代碼直接執行
* **F7 Step Into** 可以單步執行代碼,如果是函數,會進入函數內部
### 2.5 函數的文檔注釋
* 在開發中,如果希望給函數添加注釋,應該在 **定義函數** 的下方,使用 **連續的三對引號**
* 在 **連續的三對引號** 之間編寫對函數的說明文字
* 在 **函數調用** 位置,使用快捷鍵 `CTRL + Q` 可以查看函數的說明信息
> 注意:因為 **函數體相對比較獨立**,**函數定義的上方**,應該和其他代碼(包括注釋)保留 **兩個空行**
## 03. 函數的參數
**演練需求**
1. 開發一個 `sum_2_num` 的函數
2. 函數能夠實現 **兩個數字的求和** 功能
演練代碼如下:
```python
def sum_2_num():
num1 = 10
num2 = 20
result = num1 + num2
print("%d + %d = %d" % (num1, num2, result))
sum_2_num()
```
**思考一下存在什么問題**
> 函數只能處理 **固定數值** 的相加
**如何解決?**
* 如果能夠把需要計算的數字,在調用函數時,傳遞到函數內部就好了!
### 3.1 函數參數的使用
* 在函數名的后面的小括號內部填寫 **參數**
* 多個參數之間使用 `,` 分隔
```python
def sum_2_num(num1, num2):
result = num1 + num2
print("%d + %d = %d" % (num1, num2, result))
sum_2_num(50, 20)
```
### 3.2 參數的作用
* **函數**,把 **具有獨立功能的代碼塊** 組織為一個小模塊,在需要的時候 **調用**
* **函數的參數**,增加函數的 **通用性**,針對 **相同的數據處理邏輯**,能夠 **適應更多的數據**
1. 在函數 **內部**,把參數當做 **變量** 使用,進行需要的數據處理
2. 函數調用時,按照函數定義的**參數順序**,把 **希望在函數內部處理的數據**,**通過參數** 傳遞
### 3.3 形參和實參
* **形參**:**定義** 函數時,小括號中的參數,是用來接收參數用的,在函數內部 **作為變量使用**
* **實參**:**調用** 函數時,小括號中的參數,是用來把數據傳遞到 **函數內部** 用的
## 04. 函數的返回值
* 在程序開發中,有時候,會希望 **一個函數執行結束后,告訴調用者一個結果**,以便調用者針對具體的結果做后續的處理
* **返回值** 是函數 **完成工作**后,**最后** 給調用者的 **一個結果**
* 在函數中使用 `return` 關鍵字可以返回結果
* 調用函數一方,可以 **使用變量** 來 **接收** 函數的返回結果
> 注意:`return` 表示返回,后續的代碼都不會被執行
```python
def sum_2_num(num1, num2):
"""對兩個數字的求和"""
return num1 + num2
# 調用函數,并使用 result 變量接收計算結果
result = sum_2_num(10, 20)
print("計算結果是 %d" % result)
```
## 05. 函數的嵌套調用
* 一個函數里面 **又調用** 了 **另外一個函數**,這就是 **函數嵌套調用**
* 如果函數 `test2` 中,調用了另外一個函數 `test1`
* 那么執行到調用 `test1` 函數時,會先把函數 `test1` 中的任務都執行完
* 才會回到 `test2` 中調用函數 `test1` 的位置,繼續執行后續的代碼
```python
def test1():
print("*" * 50)
print("test 1")
print("*" * 50)
def test2():
print("-" * 50)
print("test 2")
test1()
print("-" * 50)
test2()
```
### 函數嵌套的演練 —— 打印分隔線
> 體會一下工作中 **需求是多變** 的
**需求 1**
* 定義一個 `print_line` 函數能夠打印 `*` 組成的 **一條分隔線**
```python
def print_line(char):
print("*" * 50)
```
**需求 2**
* 定義一個函數能夠打印 **由任意字符組成** 的分隔線
```python
def print_line(char):
print(char * 50)
```
**需求 3**
* 定義一個函數能夠打印 **任意重復次數** 的分隔線
```python
def print_line(char, times):
print(char * times)
```
**需求 4**
* 定義一個函數能夠打印 **5 行** 的分隔線,分隔線要求符合**需求 3**
> 提示:工作中針對需求的變化,應該冷靜思考,**不要輕易修改之前已經完成的,能夠正常執行的函數**!
```python
def print_line(char, times):
print(char * times)
def print_lines(char, times):
row = 0
while row < 5:
print_line(char, times)
row += 1
```
## 06. 使用模塊中的函數
> **模塊是 Python 程序架構的一個核心概念**
* **模塊** 就好比是 **工具包**,要想使用這個工具包中的工具,就需要 **導入 import** 這個模塊
* 每一個以擴展名 `py` 結尾的 `Python` 源代碼文件都是一個 **模塊**
* 在模塊中定義的 **全局變量** 、 **函數** 都是模塊能夠提供給外界直接使用的工具
### 6.1 第一個模塊體驗
**步驟**
* 新建 `hm_10_分隔線模塊.py`
* 復制 `hm_09_打印多條分隔線.py` 中的內容,**最后一行 `print` 代碼除外**
* 增加一個字符串變量
```python
name = "黑馬程序員"
```
* 新建 `hm_10_體驗模塊.py` 文件,并且編寫以下代碼:
```python
import hm_10_分隔線模塊
hm_10_分隔線模塊.print_line("-", 80)
print(hm_10_分隔線模塊.name)
```
#### 體驗小結
* 可以 **在一個 Python 文件** 中 **定義 變量 或者 函數**
* 然后在 **另外一個文件中** 使用 `import` 導入這個模塊
* 導入之后,就可以使用 `模塊名.變量` / `模塊名.函數` 的方式,使用這個模塊中定義的變量或者函數
> **模塊**可以讓 **曾經編寫過的代碼** 方便的被 **復用**!
### 6.2 模塊名也是一個標識符
* 標示符可以由 **字母**、**下劃線** 和 **數字** 組成
* **不能以數字開頭**
* **不能與關鍵字重名**
> 注意:如果在給 Python 文件起名時,**以數字開頭** 是無法在 `PyCharm` 中通過導入這個模塊的
### 6.3 Pyc 文件(了解)
> `C` 是 `compiled` **編譯過** 的意思
**操作步驟**
1. 瀏覽程序目錄會發現一個 `__pycache__` 的目錄
2. 目錄下會有一個 `hm_10_分隔線模塊.cpython-35.pyc` 文件,`cpython-35` 表示 `Python` 解釋器的版本
3. 這個 `pyc` 文件是由 Python 解釋器將 **模塊的源碼** 轉換為 **字節碼**
* `Python` 這樣保存 **字節碼** 是作為一種啟動 **速度的優化**
**字節碼**
* `Python` 在解釋源程序時是分成兩個步驟的
1. 首先處理源代碼,**編譯** 生成一個二進制 **字節碼**
2. 再對 **字節碼** 進行處理,才會生成 CPU 能夠識別的 **機器碼**
* 有了模塊的字節碼文件之后,下一次運行程序時,如果在 **上次保存字節碼之后** 沒有修改過源代碼,Python 將會加載 .pyc 文件并跳過編譯這個步驟
* 當 `Python` 重編譯時,它會自動檢查源文件和字節碼文件的時間戳
* 如果你又修改了源代碼,下次程序運行時,字節碼將自動重新創建
> 提示:有關模塊以及模塊的其他導入方式,后續課程還會逐漸展開!
>
> **模塊是 Python 程序架構的一個核心概念**
- 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