函數概述
函數的概念
函數就是把完成特定功能的一段代碼封裝起來。給該功能起一個名字(函數名)。
哪裏需要實現該功能就在哪裏調用該函數。
函數可以在任何時間任何地方調用。
函數的作用
- 函數是能完成某一功能的代碼段
- 函數是可重複執行的代碼段
- 函數方便管理和維護,便於複用
定義函數
使
用函數之前一定要先定義。
python 的函數定義非常簡單。
函數定義語法:
def 函數名([形參列表]):
函數體
說明:
1. def
是定義函數的關鍵字。(define function)
函數名是這個函數的符號(引用),調用這個函數的時候我們需要函數名。
函數名後的圓括號是必須的。
形參列表表示我們可以定義多個形參,接受函數調用時傳遞過來的參數。形參不是必須的,根據需要決定是否需要定義形參
圓括號後面必須有一個分號
:
.新起一行,必須有縮進來定義函數體。函數體就是函數每次調用的時候都會執行的代碼。
# 定義無參函數
def show():
print("我是函數內的代碼1")
print("我是函數內的代碼2")
# 定義有參函數,形參在函數內部可以作爲普通變量使用。
def show1(a, b):
print(a + b)
函數調用
定
義函數的目的是爲了讓函數做一些事情。
但是函數如果僅僅定義不會自己去執行。
時刻記住一句話,函數只有被調用才能被執行!
所以想要函數執行,必須顯示的去調用函數。
在使用函數的時候,一定要保證聲明在前,調用在後!
簡單函數調用
函數調用非常簡單,只需要: 函數名(實參)
即可。當然如果函數定義的時候沒有形參,則就不用實參。
def show():
print("我是函數內的代碼1")
print("我是函數內的代碼2")
show() # 調用函數
show() # 一個函數可以多次調用。每次調用都會執行一次函數體的代碼
show()
帶形參的函數調用
函數聲明的時候,在括號內的是形參。
那麼在調用函數的時候應該傳遞相同屬性的參數過去,函數調用的時候的參數,叫你實參!
形參和實參的個數必須匹配,但是有一種情況除外,後面再說。
def add(a, b):
print(a + b)
add(2, 3)
add(20, 3)
函數的返回值
#
4.1.return
的使用
前面定義的函數都是非常簡單的,僅僅做了一些輸出。在實際開發中,這種函數其實沒有太多的用處。
返回值在函數定義中一個非常重要的地方。
我們定義的函數可以完成一個的功能,很多情況下,功能完成之後需要給函數調用者返回一些數據,這些返回的數據就需要用到函數的返回值功能。
比如:定義一個函數可以實現兩個數的相加,然後返回給調用者計算的結果!
def add(a, b):
n = a + b
return n
print(add(3, 5))
print(add(30, 5))
說明:
1. 在函數內任何地方都可以出現return
。return
的作用就是結束函數,並把return
後面的值返回給調用者。
一旦碰到
return
,不管後面有多少代碼,不管是否處於循環中,函數都會立即結束。如果整個函數內部沒有出現
return
,則函數會自動執行到函數體最後一行代碼。如果函數體內沒有
return
,則函數運行結束的時候自動返回None
return
後面也可以不跟返回值,這種情況下主要是爲了結束函數,也會返回None
案例1:一個給定的數,判斷是否爲質數
分析:定義一個函數,這個給定的數通過參數傳遞,最後結果:是否爲質數,通過返回一個bool
值給調用者。
def is_prime(num):
for i in range(2, num):
if num % i == 0:
return False
return True
num = int(input("請輸入一個整數:"))
if is_prime(num):
print("%d 是質數!" % num)
else:
print("%d 不是是質數!" % num)
案例2:計算輸入的任意兩個數之間所有的質數的和
分析:剛纔已經定義了判斷一個是否爲質數,現在需要再定義一個可以計算兩個數之間所有的質數的和的函數,並把計算結果返回給調用者。
def is_prime(num):
for i in range(2, num):
if num % i == 0:
return False
return True
def add_prime(num1, num2):
sum = 0
for num in range(num1, num2):
if is_prime(num):
sum += num
return sum
num1 = int(input("請輸入第一個整數:"))
num2 = int(input("請輸入第2個整數:"))
print("%d 到 %d 的之間所有的質數的和是:%d" % (num1, num2, add_prime(num1, num2)))
4.2.返回多個值
使用return
一次只能返回一個值。
有些場景下,我們需要返回多個值。這種情況下我們我們可以把返回值的封裝到list
或tuple
中,接受者拿到這個list
或tuple
直接解包就可以使用了。
最好封裝在tuple
中,因爲tuple
是不可變的, 所以效率比較高。
def foo(x):
return x ** 2, x ** 3, x ** 4
x = 4
a, b, c = foo(x)
print("%d 的平方是:%d, 3次方式:%d, 4次方是:%d" % (x, a, b, c))
文檔註釋
以
前我們使用 #
來我們的代碼添加註釋,只使用單行註釋。
我們定義了一個好函數,函數的描述等信息也應該讓調用者很容易獲取到,這個時候就用到文檔註釋。
文檔註釋將來可以直接生成 api
文檔方便閱讀。
如果要給函數添加文檔註釋,直接在函數內部的首行放置一個字符串即可。字符串可以是單行的(" "或者' '
),也可以是是多行的(""" """ 或 ''' '''
)。
但是一般使用多行字符串來定義
作爲國際慣例:
註釋的第一行,一般是對函數的簡述。
然後一個空行。
然後開始進行詳細描述函數功能等。
def foo(x):
"""該函數是對參數做一些計算
計算參數的多個次冪
:param x: 要計算次冪的數字
:return: 返回2次冪、3次冪、4次冪組成的元組
"""
return x ** 2, x ** 3, x ** 4
print(foo.__doc__) # 打印函數的文檔註釋
作用域規則
有了函數之後,我們必須要面對一個作用域的問題。
比如:你現在訪問一個變量,那麼 python 解析器是怎麼查找到這個變量,並讀取到這個變量的值的呢? 依靠的就是作用域規則!
#
命名空間(namespace
)
先了解第一個概念:命名空間。
命名空間是從命名到對象的映射。當前 pytyon 的命名空間是靠字典來實現的,但是對我們來說我們並需要關注這個,而且以後也有可能會更改。
命名空間的一些例子:
1. 內置的命名集(the set of built-in names
)。這命名集(命名空間)包含了像abs()
這樣的內置函數和內置的異常名等。
在一個模塊中的全局命名集。
函數內的局部命名空間。
從某種意義上來說,一個對象的屬性集也是一個命名空間。
對命名空間來說,需要要知道最重要的一點是:在不同的命名空間的中的命名絕對沒有任何的關係!
不同的命名空間具有不同的生命週期。
包含內置命名的命名空間在 python 一啓動就被創建了,而且在 python 程序結束之前永遠不會被刪除。
一個模塊的全局命名空間是在這個模塊被讀入到內存的時候被創建。正常情況下全局命名空間也是在 python 結束之後纔會被刪除。
局部命名空間是在函數被調用的時候被創建。當函數結束或者拋了異常這個函數又沒處理的時候,局部命名空間會被銷燬。(用忘記這個局部命名空間可能更恰當)
當然,函數遞歸調用時,每調用一次都會創建一個新的局部命名空間。
#
作用域(scope
)
作用域就是 python 程序的一塊文本區域,在這個區域內,可以直接訪問(Directly accessible)命名空間。
直接訪問的意思就是:當你訪問一個絕對的命名的時候,直接在命名空間中查找
儘管作用域的定義是靜態的,但是作用域的使用(查找變量)卻是動態的。
在代碼執行的任何時間,至少有 3 個嵌套的作用域,這些作用域的命名空間可以直接訪問。
內部作用域(局部作用域)。包含了所有的局部命名,在訪問變量的時候,首先在內部作用域中查找。
然後是嵌套函數的外層作用域。在這裏搜索非局部,但也是非全局的命名。(在 python 中允許在函數中定義函數的)
然後是包含當前模塊的全局作用域。
最後搜索的是最外層的創建內置命名的作用域。
#
作用域在 python 中的具體應用
6.3.1.訪問局部作用域
def foo():
a = 20
print(a)
foo()
說明:
函數內部訪問變量a
, 先在foo
函數內部查找。因爲 a
確實是在函數內部聲明的變量,然後就找到了a
6.3.2.訪問外部作用域
a = 100
def foo():
print(a)
foo()
說明:
在
foo
函數內部,我們直接去訪問一個變量a
,那麼就會沿着作用域從內向外開始查找a
先查找
foo
的局部作用域,發現沒有a
。然後繼續去foo
函數的外部作用域,這個例子中就直接到了當前模塊的全局作用域,所以找到了 a, 所以就輸出了全局作用域中a
的值!
6.3.3.訪問外部函數的作用域
def outer():
a = 20
def inner():
print(a)
inner()
outer()
說明:
我們在一個函數的內部聲明瞭一函數,這種函數嵌套在 python 中是允許的。
內部函數
inner
執行的時候,訪問變量a
,現在inner
內部找變量a
, 沒有找到,然後去他外部的函數中找變量a
, 找到後, 就直接輸出了他的值
#
python 針對修改變量值的特殊情況
6.4.1.只能修改局部變量
在 python 的函數中, 修改一個變量的值的時候,永遠操作的是局部變量
爲什麼會這樣呢?
這其實是由 python 定義變量的方式所決定的.
python 不需要顯示的去定義變量,直接賦值的時候如果變量不存在直接就定義了.
如果在函數內部可以直接修改外部作用域變量的值,則就無法定義一個同名變量了.
所以, python 才規定不能在函數內部直接修改外部作用域變量的值.
a = 10
def foo():
a = 20 # 這裏其實是新創建了一個局部變量 a .並不是修改的全局作用域的變量 a
print(a) # 根據作用域的查找規則,這裏訪問的是局部變量 a
foo()
print(a) # 根據作用域查找規則,這裏訪問的是全局作用域的 a
6.4.2.變量必須先賦值才能使用
看下面的代碼:
a = 10
def foo():
a = a + 2
foo()
說明:
a = a + 2
這行代碼有問題. 爲什麼?
首先要搞清楚 a + 2
中的a
是局部變量還是全局變量?
是局部變量a
!
在解釋器運行這個函數的時候, 已經檢測到函數內部有創建局部變量a
, 所以這個時候你訪問到的一定是局部變量a
.
a + 2
中的局部變量a
還沒有 被賦值,所以和 2 相加拋出了異常.UnboundLocalError
a = 10
def foo():
print(a)
a = 20
foo()
說明:
原因和前面的一樣的.
解析器已經檢測到你後面會聲明局部變量a
, 所以print(a)
中的 a
仍然是局部變量.但是還沒有賦值,所以就拋異常了
總結:
在函數內部如果你定義了局部變量,那麼你在任何地方都沒有辦法訪問到函數外部作用域的同名變量.
#
函數內修改全局變量
通過前面的學習, 正常情況下我們知道了在函數內部沒有辦法修改全局變量的值!
但是這只是正常情況下!
如果我們有在函數內部修改全局變量值的需求怎麼辦?
也是可以的, 但是我們需要做些小動作: 使用關鍵字global
a = 10
def foo():
global a # 告訴 pytyon 解析器, a 以後就是全局變量了
a = 20
foo()
print(a) # 20
說明:
global
後面跟上全局變量的名, 那麼在後面的代碼中就可以使用全局變量了.如果有多個全局變量需要修改,
global
可以同時定義多個全局變量.global a, b, c
#
內部函數修改外部函數的局部變量
當用到函數嵌套的時候, 內部函數正常情況下也是無法修改外部函數的局部變量, 只能訪問讀取.
如果想修改怎麼辦?
使用關鍵字:nonlocal
a = 10
def outer():
a = 20
def inner():
nonlocal a # 把 a 綁定到外部函數的局部變量 a 上
a = 30
inner()
print("outer 的局部變量a:" + str(a)) # 30 被內部函數 inner 修改了
outer()
print("全局變量a:" + str(a))