### 如何定義函數
在Python中,定義一個函數要使用`def`語句,依次寫出函數名、括號、括號中的參數和冒號:,然后,在縮進塊中編寫函數體,函數的返回值用`return`語句返回。
#### 定義求絕對值的my_abs函數
我們以自定義一個求絕對值的`my_abs`函數為例:
~~~
def my_abs(x):
if x >= 0:
return x
else:
return -x
~~~
請自行測試并調用`my_abs`看看返回結果是否正確。
請注意,函數體內部的語句在執行時,一旦執行到`return`時,函數就執行完畢,并將結果返回。因此,函數內部通過條件判斷和循環可以實現非常復雜的邏輯。
如果沒有`return`語句,函數執行完畢后也會返回結果,只是結果為`None`。
`return None`可以簡寫為`return`。
在Python交互環境中定義函數時,注意Python會出現...的提示。函數定義結束后需要按兩次回車重新回到`>>>`提示符下:
#### [視頻地址](http://www.bilibili.com/video/av5642971/?zw)(地址:http://www.bilibili.com/video/av5642971/?zw)
如果你已經把`my_abs()`的函數定義保存為`abstest.py`文件了,那么,可以在該文件的當前目錄下啟動Python解釋器,用`from abstest import my_abs`來導入`my_abs()`函數,注意`abstest`是文件名(不含`.py`擴展名):
#### [視頻地址](http://github.liaoxuefeng.com/sinaweibopy/video/py/import-abstest.mp4)(地址:http://github.liaoxuefeng.com/sinaweibopy/video/py/import-abstest.mp4)
`import`的用法在后續`模塊`一節中會詳細介紹。
### 定義空函數
如果想定義一個什么事也不做的空函數,可以用`pass`語句:
~~~
def nop():
pass
~~~
`pass`語句什么都不做,那有什么用?實際上`pass`可以用來作為占位符,比如現在還沒想好怎么寫函數的代碼,就可以先放一個`pass`,讓代碼能運行起來。
`pass`還可以用在其他語句里,比如:
~~~
if age >= 18:
pass
~~~
缺少了`pass`,代碼運行就會有語法錯誤。
### 參數檢查
調用函數時,如果參數個數不對,Python解釋器會自動檢查出來,并拋出`TypeError`:
~~~
>>> my_abs(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: my_abs() takes 1 positional argument but 2 were given
~~~
但是如果參數類型不對,Python解釋器就無法幫我們檢查。試試`my_abs`和內置函數`abs`的差別:
~~~
>>> my_abs('A')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in my_abs
TypeError: unorderable types: str() >= int()
>>> abs('A')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'
~~~
當傳入了不恰當的參數時,內置函數`abs`會檢查出參數錯誤,而我們定義的`my_abs`沒有參數檢查,會導致`if`語句出錯,出錯信息和`abs`不一樣。所以,這個函數定義不夠完善。
讓我們修改一下`my_abs`的定義,對參數類型做檢查,只允許整數和浮點數類型的參數。數據類型檢查可以用內置函數`isinstance()`實現:
~~~
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
~~~
添加了參數檢查后,如果傳入錯誤的參數類型,函數就可以拋出一個錯誤:
~~~
>>> my_abs('A')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in my_abs
TypeError: bad operand type
~~~
### 錯誤和異常處理將在后續講到。
### 返回多個值
函數可以返回多個值嗎?答案是肯定的。
比如在游戲中經常需要從一個點移動到另一個點,給出坐標、位移和角度,就可以計算出新的新的坐標:
~~~
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
~~~
`import math`語句表示導入`math`包,并允許后續代碼引用`math`包里的`sin`、`cos`等函數。
然后,我們就可以同時獲得返回值:
~~~
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0
但其實這只是一種假象,Python函數返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)
~~~
原來返回值是一個`tuple`!但是,在語法上,返回一個`tuple`可以省略括號,而多個變量可以同時接收一個`tuple`,按位置賦給對應的值,所以,`Python`的函數返回多值其實就是返回一個`tuple`,但寫起來更方便。
### 小結
#### 定義函數時,需要確定函數名和參數個數;
#### 如果有必要,可以先對參數的數據類型做檢查;
#### 函數體內部可以用return隨時返回函數結果;
#### 函數執行完畢也沒有return語句時,自動return None。
#### 函數可以同時返回多個值,但其實就是一個tuple。
### 練習
#### 請定義一個函數quadratic(a, b, c),接收3個參數,返回一元二次方程:
`ax2 + bx + c = 0`
的兩個解。
提示:計算平方根可以調用math.sqrt()函數:
~~~
>>> import math
>>> math.sqrt(2)
1.4142135623730951
~~~
### 練習參考源碼
~~~
# -*- coding: utf-8 -*-
import math
def quadratic(a, b, c):
x1 = (-b-math.sqrt(b*b-4*a*c))/(2*a)
x2 = (-b+math.sqrt(b*b-4*a*c))/(2*a)
return x1,x2
# 測試:
print(quadratic(2, 3, 1)) # => (-0.5, -1.0)
print(quadratic(1, 3, -4)) # => (1.0, -4.0)
~~~
- Python教程
- Python簡介
- 安裝Python
- Python解釋器
- 第一個 Python 程序
- 使用文本編輯器
- Python代碼運行助手
- 輸入和輸出
- 源碼
- learning.py
- Python基礎
- 數據類型和變量
- 字符串和編碼
- 使用list和tuple
- 條件判斷
- 循環
- 使用dict和set
- 函數
- 調用函數
- 定義函數
- 函數的參數
- 遞歸函數
- 高級特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函數式編程
- 高階函數
- map/reduce
- filter
- sorted
- 返回函數
- 匿名函數
- 裝飾器
- 偏函數
- Python函數式編程——偏函數(來自博客)
- 模塊
- 使用模塊
- 安裝第三方模塊
- 面向對象編程
- 類和實例
- 訪問限制
- 繼承和多態
- 獲取對象信息
- 實例屬性和類屬性
- 面向對象高級編程
- 使用__slots__
- 使用@property
- 多重繼承
- 定制類
- 使用枚舉類
- 使用元類
- 錯誤、調試和測試
- 錯誤處理
- 調試
- 單元測試
- 文檔測試
- IO編程
- 文件讀寫
- StringIO和BytesIO
- 操作文件和目錄
- 序列化
- 進程和線程
- 多進程
- 多線程
- ThreadLocal
- 進程 vs. 線程
- 分布式進程
- 正則表達式
- 常用內建模塊
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- contextlib
- XML
- HTMLParser
- urllib
- 常用第三方模塊
- PIL
- virtualenv
- 圖形界面
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 電子郵件
- SMTP發送郵件
- POP3收取郵件
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web開發
- HTTP協議簡介
- HTML簡介
- WSGI接口
- 使用Web框架
- 使用模板
- 異步IO
- 協程
- asyncio
- async/await
- aiohttp
- 實戰
- Day 1 - 搭建開發環境
- Day 2 - 編寫Web App骨架
- Day 3 - 編寫ORM
- Day 4 - 編寫Model
- Day 5 - 編寫Web框架
- Day 6 - 編寫配置文件
- Day 7 - 編寫MVC
- Day 8 - 構建前端
- Day 9 - 編寫API
- Day 10 - 用戶注冊和登錄
- Day 11 - 編寫日志創建頁
- Day 12 - 編寫日志列表頁
- Day 13 - 提升開發效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 編寫移動App
- FAQ
- 期末總結