1. 程式人生 > 實用技巧 >Python基礎講義(三):函式初步

Python基礎講義(三):函式初步

函式初步

0x01內容導圖

0x02函式定義

  • 程式的本質:問題分解,逐步求精

  • 基本原則:一個函式一個功能

  • 函式定義語法:

    def fun_name([args]):
        statements
        [return values]
    
  • 函式名要有意義,提高可讀性;

    • args:引數列表,相當於函式的輸入,定義時稱為形式引數;可以沒有引數
    • return values:返回值,相當於函式的輸出(可以有多個返回值);可以沒有返回值
  • 函式呼叫語法:function_name(args),圓括號不能省略;呼叫時,引數被稱為實際引數

程式碼示例(檔案方式,is_leap.py):判斷是否為閏年

#請熟練掌握下述框架,養成自頂向下的編碼習慣
def is_leap(year):
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

def run():
    year = int(input("please input a year:"))			#輸入
    flag = is_leap(year)								#處理
    print(f"{year}年是閏年:{flag}")						 #輸出

if __name__ == "__main__":  #是否為主模組:直接執行該模組,則為主;其他方式,如被匯入,則不是
    run()

說明:

  • Python中語句塊以縮排表示歸屬,函式是語句塊,函式體必須縮排
  • input函式是Python的標準輸入函式,從鍵盤讀入內容,返回字串形式;因此輸入數字的時候通常需要轉換函式

練習:

  1. 利用math庫,完成is_square(n)函式的定義,判斷n是否為完全平方數。
  2. 定義函式calc_area(w, h):計算長方形面積並返回

0x03變數作用範圍

  • 定義在函式內的稱為區域性變數,只能在函式內訪問

  • 定義在函式外的稱為全域性變數,儘量少用

    • 函式內可讀訪問全域性變數
    • 寫訪問,需要使用global語句宣告
    • 同名解析:就近原則
  • locals()/globals()函式:區域性/全域性名稱空間

  • 檢視函式:

    • dir函式:顯示模組中定義的函式
    • help函式:顯示函式的用法

    程式碼示例(檔案方式,scope.py):

#========================測試一============================
def disp():
    name = "world"
    print("hello" + name)

print(name)					#error,區域性變數,外面不能訪問
#========================測試二============================
#函式內可以讀訪問全域性變數
def test():
    return x + y

x, y = 2, 3
test()
#========================測試三============================
def method():
    x = 10			#賦值即定義,此處x為區域性變數
    print(x)		#就近原則,x為區域性變數

x = 5				#此處x為全域性變數
method()
print(x)			#列印全域性變數x,其值仍為5
#========================測試四============================
def test2():
    #global x
    x += 1		#報錯,區域性變數未賦值就使用
    print(x)

x = 5
test2()
print(x)
"""錯誤解析:展開x = x + 1,Python從左開始解析,第1次遇到x,由於出現在賦值號左邊,x被解釋為區域性變數(賦值即定義);繼續解析遇到第2個x,就近原則訪問區域性變數x,而這時x還未完成賦值,因此報錯。"""
#修改辦法:第16行取消註釋,將x宣告為全域性,然後才能修改x的值

0x04測試驅動開發

  • 測試先行,結對編碼

    • 對外介面設計:先思考函式怎麼用,會返回什麼結果,即測試用例設計
    • 程式碼實現:讓測試通過
  • Python自帶測試庫:doctest

    • 嵌入式:利用文件註釋(三引號註釋)

    程式碼示例(閏年判斷為例):

    def is_leap(year):
        """
        判斷year是否為閏年,用法如下:
        >>> is_leap(2000)
        True
        >>> is_leap(1998)
        False
        >>> is_leap(1900)
        False
        >>> is_leap(2020)
        True
        """
        return year % 4 == 0				#故意實現錯誤
    
    if __name__ == "__main__":
        import doctest
        doctest.testmod(verbose=True)
    

測試部分結果如下:

...
Failed example:
    is_leap(1900)
Expected:
    False
Got:
    True
Trying:
    is_leap(2020)
Expecting:
    True
ok
1 items had failures:
   1 of   4 in __main__.is_leap
4 tests in 2 items.
3 passed and 1 failed.
***Test Failed*** 1 failures.

上述結果顯示,我們的3個測試都通過了,但1900年測試失敗,不是閏年卻返回了是,程式碼實現應該和前面的例子相同,修改後再測試可以看到:

1 items passed all tests:
   4 tests in __main__.is_leap
4 tests in 2 items.
4 passed and 0 failed.
Test passed.
  • 獨立式:測試用例單獨用txt檔案儲存,程式碼和用例分開(儲存在同一目錄下)

程式碼示例(一元二次方程求解),測試檔案solve_equation_test_cases.txt如下:

>>> from solve_equation import *
>>> solve_equation(1, -2, 1)
(1.0, 1.0)
>>> solve_equation(1, 1, -6)
(2.0, -3.0)
>>> solve_equation(2, -1, -3)
(1.5, -1.0)

程式碼檔案solve_equation.py如下:

def solve_equation(a, b, c):
    import math
    delta = b*b - 4*a*c
    delta = math.sqrt(delta)
    x1 = (-b + delta) / (2*a)
    x2 = (-b - delta) / (2*a)
    return x1, x2

執行方式:命令列下,切換到對應目錄,然後執行命令python -m doctest -v solve_equation_test_cases.txt,其執行結果如下:

Trying:
    from solve_equation import *
Expecting nothing
ok
Trying:
    solve_equation(1, -2, 1)
Expecting:
    (1.0, 1.0)
ok
Trying:
    solve_equation(1, 1, -6)
Expecting:
    (2.0, -3.0)
ok
Trying:
    solve_equation(2, -1, -3)
Expecting:
    (1.5, -1.0)
ok
1 items passed all tests:
   4 tests in solve_equation_test_cases.txt
4 tests in 1 items.
4 passed and 0 failed.
Test passed.

綜上,不管是嵌入式還是獨立式,使用doctest進行測試驅動開發,明顯的一個好處就是測試用例可以儲存,可以反覆進行測試,特別是我們修改程式碼的時候,可以保證之前通過的測試必須再通過。

0x05小結

  • 理解模組化思想,熟練掌握函式定義語法
  • 掌握變數的作用範圍
  • 理解測試驅動開發,掌握doctest庫的使用