python入門系列10:函數1

函數概述

函數的概念

函數就是把完成特定功能的一段代碼封裝起來。給該功能起一個名字(函數名)。

哪裏需要實現該功能就在哪裏調用該函數。

函數可以在任何時間任何地方調用。


函數的作用

  • 函數是能完成某一功能的代碼段
  • 函數是可重複執行的代碼段
  • 函數方便管理和維護,便於複用

定義函數

使
用函數之前一定要先定義。

python 的函數定義非常簡單。

函數定義語法:

def 函數名([形參列表]):
函數體

說明:
1. def 是定義函數的關鍵字。(define function)

  1. 函數名是這個函數的符號(引用),調用這個函數的時候我們需要函數名。

  2. 函數名後的圓括號是必須的。

  3. 形參列表表示我們可以定義多個形參,接受函數調用時傳遞過來的參數。形參不是必須的,根據需要決定是否需要定義形參

  4. 圓括號後面必須有一個分號:.

  5. 新起一行,必須有縮進來定義函數體。函數體就是函數每次調用的時候都會執行的代碼。


# 定義無參函數
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. 在函數內任何地方都可以出現returnreturn的作用就是結束函數,並把return後面的值返回給調用者。

  1. 一旦碰到 return,不管後面有多少代碼,不管是否處於循環中,函數都會立即結束。

  2. 如果整個函數內部沒有出現return,則函數會自動執行到函數體最後一行代碼。

  3. 如果函數體內沒有return,則函數運行結束的時候自動返回None

  4. 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一次只能返回一個值。

有些場景下,我們需要返回多個值。這種情況下我們我們可以把返回值的封裝到listtuple中,接受者拿到這個listtuple直接解包就可以使用了。

最好封裝在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 文檔方便閱讀。

如果要給函數添加文檔註釋,直接在函數內部的首行放置一個字符串即可。字符串可以是單行的(" "或者' '),也可以是是多行的(""" """ 或 ''' ''')。

但是一般使用多行字符串來定義

作爲國際慣例:

  1. 註釋的第一行,一般是對函數的簡述。

  2. 然後一個空行。

  3. 然後開始進行詳細描述函數功能等。

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()這樣的內置函數和內置的異常名等。

  1. 在一個模塊中的全局命名集。

  2. 函數內的局部命名空間。

  3. 從某種意義上來說,一個對象的屬性集也是一個命名空間。

對命名空間來說,需要要知道最重要的一點是:在不同的命名空間的中的命名絕對沒有任何的關係!


不同的命名空間具有不同的生命週期。

  1. 包含內置命名的命名空間在 python 一啓動就被創建了,而且在 python 程序結束之前永遠不會被刪除。

  2. 一個模塊的全局命名空間是在這個模塊被讀入到內存的時候被創建。正常情況下全局命名空間也是在 python 結束之後纔會被刪除。

  3. 局部命名空間是在函數被調用的時候被創建。當函數結束或者拋了異常這個函數又沒處理的時候,局部命名空間會被銷燬。(用忘記這個局部命名空間可能更恰當)

  4. 當然,函數遞歸調用時,每調用一次都會創建一個新的局部命名空間。


#
作用域(scope)

作用域就是 python 程序的一塊文本區域,在這個區域內,可以直接訪問(Directly accessible)命名空間。

直接訪問的意思就是:當你訪問一個絕對的命名的時候,直接在命名空間中查找

儘管作用域的定義是靜態的,但是作用域的使用(查找變量)卻是動態的。


在代碼執行的任何時間,至少有 3 個嵌套的作用域,這些作用域的命名空間可以直接訪問。

  1. 內部作用域(局部作用域)。包含了所有的局部命名,在訪問變量的時候,首先在內部作用域中查找。

  2. 然後是嵌套函數的外層作用域。在這裏搜索非局部,但也是非全局的命名。(在 python 中允許在函數中定義函數的)

  3. 然後是包含當前模塊的全局作用域。

  4. 最後搜索的是最外層的創建內置命名的作用域。


#
作用域在 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()

說明:

  1. foo函數內部,我們直接去訪問一個變量 a,那麼就會沿着作用域從內向外開始查找a

  2. 先查找foo的局部作用域,發現沒有a。然後繼續去foo函數的外部作用域,這個例子中就直接到了當前模塊的全局作用域,所以找到了 a, 所以就輸出了全局作用域中a的值!


6.3.3.訪問外部函數的作用域

def outer():
a = 20

def inner():
print(a)

inner()


outer()

說明:

  1. 我們在一個函數的內部聲明瞭一函數,這種函數嵌套在 python 中是允許的。

  2. 內部函數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

說明:

  1. global 後面跟上全局變量的名, 那麼在後面的代碼中就可以使用全局變量了.

  2. 如果有多個全局變量需要修改, 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))


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章