Python學習筆記系列之011:函數

来源:https://www.cnblogs.com/salmond/archive/2018/04/15/8845806.html
-Advertisement-
Play Games

導讀: 1.函數的定義 2.函數的文檔註釋 3.函數的參數與調用 4.函數參數的類型(重點) 5.函數的返回值 6.函數的嵌套調用 7.局部變數和全局變數 所謂函數,就是把 具有獨立功能的代碼塊 組織為一個小模塊,在需要的時候 調用。 函數的使用包含兩個步驟: 1.定義函數 —— 封裝 獨立的功能 ...


導讀:

1.函數的定義

2.函數的文檔註釋

3.函數的參數與調用

4.函數參數的類型(重點)

5.函數的返回值

6.函數的嵌套調用

7.局部變數和全局變數

 

所謂函數,就是把 具有獨立功能的代碼塊 組織為一個小模塊,在需要的時候 調用

函數的使用包含兩個步驟:

1.定義函數 —— 封裝 獨立的功能 

2.調用函數 —— 享受 封裝 的成果 

函數的作用:在開發程式時,使用函數可以提高編寫的效率以及代碼的 重用 。

函數的特點:功能性;隱藏細節;避免編寫重覆的代碼

 

一、函數的定義

定義函數使用關鍵字 def ,後接函數名和放在圓括弧( )中的可選參數列表,函數內容以冒號起始並且縮進。一般格式如下:

def 函數名(參數列表):

    """文檔字元串"""

        函數體

    return [expression]

註意:參數列表可選,文檔字元串可選,return語句可選。

示例:

def fib(n):

    """Print a Fibonacci series"""

    a, b = 0, 1

    while b < n:

        print(b, end=' ')

        a, b = b, a+b

    print()

fib(2000) # call

f = fib # assignment

f(2000)
code demo

函數名的值是一種用戶自定義的函數類型。函數名的值可以被賦予另一個名字,使其也能作為函數使用。

1.函數名稱 應該能夠表達 函數封裝代碼 的功能,方便後續的調用 

2.函數名稱 的命名應該 符合 標識符的命名規則

二、函數的文檔註釋

函數體的第一個語句可以是三引號括起來的字元串, 這個字元串就是函數的文檔字元串,或稱為docstring 。我們可以使用 print(function.__doc__) 輸出文檔:

def fun():

    """Some information of this function.

This is documentation string."""

    return

    print(fun.__doc__)

文檔字元串主要用於描述一些關於函數的信息,讓用戶交互地瀏覽和輸出。建議養成在代碼中添加文檔字元串的好習慣。

在 函數調用 位置,使用help(函數名)快捷鍵 CTRL + Q 可以查看函數的說明信息。

註意

因為 函數體相對比較獨立,函數定義的上方 ,應該和其他代碼(包括註釋)保留兩個空行。

小技巧

在給函數添加註釋時,可以滑鼠游標放在 函數名 上,pycharm上會出現一個小燈,選擇 ”insert documentation string stub" 會自動添加註釋模板。

三、函數的參數與調用

3.1 參數的使用

在函數名的後面的小括弧內部填寫 參數;多個參數之間使用 , 分隔。

def sum_2_num(num1, num2):

    result = num1 + num2   

    print("%d + %d = %d" % (num1, num2, result))

sum_2_num(50, 20)

3.2 參數的作用 

函數的參數,增加函數的 通用性,針對 相同的數據處理邏輯,能夠 適應更多的數據 在函數內部,把參數當做 變數 使用,進行需要的數據處理。

函數調用時,按照函數定義的參數順序,把 希望在函數內部處理的數據,通過參數傳遞。

3.3 形參和實參 

  形參:定義函數時,小括弧中的參數,是用來接收參數用的,在函數內部作為變數使用。

  實參:調用函數時,小括弧中的參數,是用來把數據傳遞到函數內部用的。

3.4 函數的調用

通過 函數名() 即可完成對函數的調用。

# 只有在調用函數時,之前定義的函數才會被執行。

# 函數執行完成之後,會重新回到之前的程式中,繼續執行後續的代碼。

# 調用時,實參的個數和先後順序應該和定義函數中要求的一致。

註意

不能將 函數調用 放在 函數定義 的上方! 

因為在 使用函數名 調用函數之前,必須要保證Python 已經知道函數的存在,否則控制台會提示 NameError: name 'say_hello' is not defined (名稱錯誤:say_hello 這個名字沒有被定義)

 

四、函數參數的類型

4.1 必須參數

def add(x, y):

    result = x + y

    return result

c = add(2, 3)

print(c)  # 5
View Code

 

4.2 預設參數(預設參數)

在形參中預設有值的參數,稱之為預設參數。

def add(x, y = 3, z=4):

    result = x + y +z

    return result

c = add(2)   #c = add(x = 2)

print(c)  # 9
View Code

預設參數必須放在必須參數之後。

def add(x, y = 3, z=4):

    result = x + y +z

    return result

c = add(2, 5)

print(c)  # 11
View Code

調用時如果要改變預設參數的值,最好,使用關鍵字參數。

def add(x, y = 3, z=4):

    result = x + y +z

    return result

c = add(2, z= 10)   #c = add(x = 2)

print(c)  # 15
View Code

關鍵字參數必須放在其他參數之後。

def add(x, y = 3, z=4, w=5):

    result = x + y +z

    return result

c = add(2, y= 6, 4, w=7)   #c = add(x = 2)

print(c)  # 報錯
View Code

註意:通常情況下預設值只被計算一次,但如果預設值是一個可變對象時會有所不同, 如列表, 字典, 或 大多類的對象時。例如,下麵的函數在隨後的調用中會累積參數值:

def fun(a, L=[]):

    L.append(a)

    print(L)

fun(1) # 輸出[1]

fun(2) # 輸出[1, 2]

fun(3) # 輸出[1, 2, 3]
View Code

 預設參數必須指向不變對象:

先定義一個函數,傳入一個 list,添加一個 END 再返回:

def add_end(L=[]):

    L.append('END')

    return L

當你正常調用時,結果似乎不錯:

>>> add_end([1, 2, 3])
[1, 2, 3, 'END']

>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']

當你使用預設參數調用時,一開始結果也是對的:

>>> add_end()
['END']

但是,再次調用 add_end()時,結果就不對了:

>>> add_end()
['END', 'END']

>>> add_end()
['END', 'END', 'END']

很多初學者很疑惑,預設參數是[],但是函數似乎每次都“記住了”上次添加了'END'後的 list。

原因解釋如下:

Python 函數在定義的時候,預設參數 L 的值就被計算出來了,即[],因為預設參數 L 也是一個變數,它指向對象[],每次調用該函數,如果改變了 L 的內容,則下次調用時,預設參數的內容就變了,不再是函數定義時的[]了。

所以,定義預設參數要牢記一點:預設參數必須指向不變對象! 

 

要修改上面的例子,我們可以用 None 這個不變對象來實現:

def add_end(L=None):
    if L is None:
        L = []
        L.append('END')
        return L

現在,無論調用多少次,都不會有問題:

>>> add_end()
['END']

>>> add_end()
['END']

為什麼要設計 str、 None 這樣的不變對象呢?因為不變對象一旦創建,對象內部的數據就不能修改,這樣就減少了由於修改數據導致的錯誤。

此外,由於對象不變,多任務環境下同時讀取對象不需要加鎖,同時讀一點問題都沒有。我們在編寫程式時,如果可以設計一個不變對象,那就儘量設計成不變對象。

4.3 關鍵字參數

函數也可以通過 keyword=value 形式的關鍵字參數來調用,因為明確指出了對應關係,所以參數的順序也就無關緊要了。

使用關鍵字參數,可以無視參數順序。

def add(x, y):
    result = x + y
    return result
c = add(y = 3, x = 2)
print(c)  # 5
View Code

4.4 可變參數(不定長參數)

有時可能需要一個函數能處理比當初聲明時更多的參數, 這些參數叫做不定長參數,聲明時不會命名。

通過在形參前加一個星號(*)或兩個星號(**)來指定函數可以接收任意數量的實參。

基本法如下:

def functionname([formal_args,] *args, **kwargs):

   """函數_文檔字元串"""

   function_suite

   return [expression]

def fun(*args):
    print(type(args))
    print(args)

fun(1,2,3,4,5,6)

# 輸出:
# <class 'tuple'>
# (1, 2, 3, 4, 5, 6)

 

def fun(**args):
    print(type(args))
    print(args)

fun(a=1,b=2,c=3,d=4,e=5)

# 輸出:
# <class 'dict'>
# {'d': 4, 'e': 5, 'b': 2, 'c': 3, 'a': 1}
View Code

從兩個示例的輸出可以看出:

當參數形如 *args 時,會存放所有未命名的變數參數,傳遞給函數的任意個實參會按位置被包裝進一個元組(tuple

當參數形如 **kwargs 時,會存放命名參數,傳遞給函數的任意個 key=value 實參會被包裝進一個字典(dict

>>> def fun(a, b, *args, **kwargs):
...     """可變參數演示示例""" 
...     print "a =", a
...     print "b =", b
...     print "args =", args
...     print "kwargs: "
...     for key, value in kwargs.items():
...         print key, "=", value
...

>>> fun(1, 2, 3, 4, 5, m=6, n=7, p=8)  # 註意傳遞的參數對應
a = 1
b = 2
args = (3, 4, 5)
kwargs:
p = 8
m = 6
n = 7


>>> c = (3, 4, 5)
>>> d = {"m":6, "n":7, "p":8}
>>> fun(1, 2, *c, **d)    # 註意元組與字典的傳參方式
# 結果同上 


>>> fun(1, 2, c, d) # 註意不加星號與上面的區別
a = 1
b = 2
args = ((3, 4, 5), {'p': 8, 'm': 6, 'n': 7})
kwargs:
View Code

如果很多個值都是不定長參數,那麼這種情況下,可以將預設參數放到*args的後面,但如果有**kwargs的話,**kwargs必須是最後的。

def sum_nums_3(a, *args, b=22, c=33, **kwargs):
    print(a)
    print(b)
    print(c)
    print(args)
    print(kwargs)


sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)

# 100
# 1
# 2
# (200, 300, 400, 500, 600, 700)
# {'mm': 800, 'nn': 900}
View Code

說明:

如果很多個值都是不定長參數,那麼這種情況下,可以將預設參數放到 *args的後面, 但如果有**kwargs的話,**kwargs必須是最後的。

4.5 解包參數

上一點說到傳遞任意數量的實參時會將它們打包進一個元組或字典,當然有打包也就有解包(unpacking)。通過 單星號和雙星號對List、Tuple和Dictionary進行解包:

def fun(a=1, b=2, c=3):
    print(a+b+c)

fun() # 正常調用
list1 = [11, 22, 33]
dict1 = {'a':40, 'b':50, 'c':60}

fun(*list1) # 解包列表
fun(**dict1) # 解包字典

# 輸出:
# 6
# 66
# 150
View Code

註:*用於解包Sequence,**用於解包字典。需要拆的數據的個數要與變數的個數相同,否則程式會異常。解包字典會得到一系列的 key=value ,故本質上就是使用關鍵字參數調用函數。

解包:

d = 1, 2, 3
print(type(d)) # <class 'tuple'>
a, b, c = d
print(a) # 1
print(b) # 2
print(c) # 3
View Code

 

4.6 命名關鍵字參數

對於關鍵字參數,函數的調用者可以傳入任意不受限制的關鍵字參數。至於到底傳入了哪些,就需要在函數內部通過 kw 檢查。

仍以 person()函數為例,我們希望檢查是否有 city 和 job 參數:

def person(name, age, **kw):
    if 'city' in kw:
       # 有 city 參數
       pass

if 'job' in kw:
       # 有 job 參數
       pass
       print('name:', name, 'age:', age, 'other:', kw)

但是調用者仍可以傳入不受限制的關鍵字參數:
>>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)

如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收 city 和 job 作為關鍵字參數。這種方式定義的函數如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

和關鍵字參數**kw 不同,命名關鍵字參數需要一個特殊分隔符*, *後面

的參數被視為命名關鍵字參數。

調用方式如下:

>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

命名關鍵字參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,調用將報錯:

>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given

由於調用時缺少參數名 city 和 job, Python 解釋器把這 4 個參數均視為位置參數,但 person()函數僅接受 2 個位置參數。

命名關鍵字參數可以有預設值,從而簡化調用:

def person(name, age, *, city='Beijing', job):
    print(name, age, city, job)

由於命名關鍵字參數 city 具有預設值,調用時,可不傳入 city 參數:

>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer

使用命名關鍵字參數時,要特別註意, *不是參數,而是特殊分隔符。

如果缺少*, Python 解釋器將無法識別位置參數和命名關鍵字參數:

def person(name, age, city, job):
    # 缺少 *, city 和 job 被視為位置參數
    pass

可變參數無法和命名關鍵字參數混合。

五、函數的返回值

在程式開發中,有時候,會希望一個函數執行結束後,告訴調用者一個結果,以便調用者針對具體的結果做後續的處理。

返回值是函數完成工作後,最後給調用者的一個結果。在函數中使用 return 關鍵字可以返回結果。

調用函數一方,可以使用變數來接收函數的返回結果。

return後面可以是元組,列表、字典等,只要是能夠存儲多個數據的類型,就可以一次性返回多個數據。

如果return後面有多個數據,那麼預設是元組。

如果函數沒有返回值,仍然為變數賦值,則變數的值為None。

註意:return 表示返回,後續的代碼都不會被執行。

def sum_2_num(num1, num2):
    """對兩個數字的求和"""
    return num1 + num2


# 調用函數,並使用result變數接收計算結果
result = sum_2_num(10, 20)
print("計算結果是 %d" % result)

python中我們可不可以返回多個值?

>>> def divid(a, b):
...     shang = a//b
...     yushu = a%b
...     return shang, yushu
...

>>> sh, yu = divid(5, 2)
>>> sh
5
>>> yu
1

本質是利用了元組。

六、函數的嵌套調用

一個函數裡面 又調用 了 另外一個函數,這就是 函數嵌套調用。

如果函數test2中,調用了另外一個函數test1那麼執行到調用 test1 函數時,會先把函數test1 中的任務都執行完,才會回到 test2 中調用函數 test1 的位置,繼續執行後續的代碼。

def test1():
    print("*" * 20)

def test2():
    print("-" * 20)
    test1()
    print("-" * 20)


test2()

結果:
--------------------
********************
--------------------
View Code

6.1 列印分隔線案例

需求 1 

  定義一個函數能夠列印 任意重覆次數 的分隔線。

def print_line(char, times):
    print(char * times)
View Code

需求 2

  定義一個函數能夠列印 5行 的分隔線,分隔線要求符合需求1。 

  提示:工作中針對需求的變化,應該冷靜思考, 不要輕易修改之前已經完成的,能夠正常執行的函數! 

def print_line(char, times):
    print(char * times)


def print_lines(char, times):
    row = 0
    while row < 5:
        print_line(char, times)
        row += 1

6.2 求3個數的平均值

需求1

求3個數的和

def sum3Number(a,b,c):
    return a+b+c # return 的後面可以是數值,也可是一個表達式
View Code

需求2

完成對3個數求平均值

def average3Number(a,b,c):

    # 因為sum3Number函數已經完成了3個數的就和,所以只需調用即可
    # 即把接收到的3個數,當做實參傳遞即可
    sumResult = sum3Number(a,b,c)
    aveResult = sumResult/3.0
    return aveResult


# 調用函數,完成對3個數求平均值
result = average3Number(11,2,55)
print("average is %d"%result)
View Code

七、局部變數和全局變數

局部變數

局部變數,就是在函數內部定義的變數。

不同的函數,可以定義相同的名字的局部變數,但是各用個的不會產生影響。

局部變數的作用,為了臨時保存數據需要在函數中定義變數來進行存儲,這就是它的作用。

全局變數

在函數外邊定義的變數叫做全局變數。

全局變數能夠在所有的函數中進行訪問。

如果在函數中修改全局變數,那麼就需要使用global進行聲明,否則出錯。

如果全局變數的名字和局部變數的名字相同,那麼使用的是局部變數的,小技巧:強龍不壓地頭蛇。

 

在函數內部定義的變數擁有一個局部作用域,在函數外定義的擁有全局作用域。

註意:在函數內部可以引用全局變數,但無法對其賦值(除非用 global 進行聲明)。

a = 5   #全局變數a

def func1():
    print('func1() print a=',a)


def func2():
    a = 21 # 局部變數a
    print('func2() print a =', a)


def func3():
    global a
    a = 10 # 修改全局變數a
    print('func3() print a =', a)


func1()  #5
func2()  #21
func3()  #10
print('the global a =', a)  #10
View Code

 

可變類型的全局變數

在函數中不使用global聲明全局變數時不能修改全局變數的本質是不能修改全局變數的指向,即不能將全局變數指向新的數據。

對於不可變類型的全局變數來說,因其指向的數據不能修改,所以不使用global時無法修改全局變數。

對於可變類型的全局變數來說,因其指向的數據可以修改,所以不使用global時也可修改全局變數。

a = 1
def f():
    a += 1
    print(a)


f()  # 報錯


li = [1,]
def f2():
    li.append(1)
    print (li)


f2()  # [1, 1]
print(li)  # [1, 1]

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • n 表格和表單的嵌套順序 n 單行文本域 語法格式:<input type=”text” 屬性=”值”> 常用屬性 l Name:文本框的名字。命名規則:可以包含字母、數字、下劃線,只能以字母開頭。 l Type:表單元素的類型。 l Value:文本框中預設的值。 l Size:文本框的長度,以“ ...
  • html2canvas是一個將html元素生成canvas的庫,繪製的canvas大部分樣式和CSS一致。比如截止1.0.0-alpha.12,虛線邊框依然繪製為實線,border-collapse依然有問題。 這裡根據github issues里的一個思路,模擬實現了dashed邊框效果。 適用情 ...
  • 測試 ...
  • 前面的話 React是如今熱門的兩大前端框架之一,它設計思路獨特,性能卓越,邏輯簡單,受到了大量開發者的喜愛。Vue的基本思路是基於HTML模板的擴展,而React的基本思路是基於JS語言的擴展。由於Vue的寫法更接近於傳統,所以對於習慣了HTML的開發者更容易接受;而React中的JSX語法需要一 ...
  • Pencil是個好項目。使用Electron作為運行環境,如同Vs Code一樣,將JS跨平臺桌面應用做了一個好的示範。個人很喜歡這種方式,畢竟多年來關註Web全棧開發,有一種JS一統天下的感覺。我的運行環境是Mac book + vscode。 1. 首先是下載Pencil項目 2. 用vs co ...
  • 上周我們免費送出了6本關於 Python 的重量級技術書籍,推出後反響特別強烈,有一個和最後一名僅差了一個贊,不過我們還是額外加送了一本送給這位朋友,以資鼓勵,從另一面也可以看出Java程式猿對Python的熱愛程式。 這次我們繼續發力,送的書籍更貼近Java技術專業,更符合大部分Java碼農剛需! ...
  • Anatoly lives in the university dorm as many other students do. As you know, cockroaches are also living there together with students. Cockroaches mig ...
  • Description 小Q在電子工藝實習課上學習焊接電路板。一塊電路板由若幹個元件組成,我們不妨稱之為節點,並將其用數 字1,2,3….進行標號。電路板的各個節點由若幹不相交的導線相連接,且對於電路板的任何兩個節點,都存在且僅 存在一條通路(通路指連接兩個元件的導線序列)。在電路板上存在一個特殊的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...