1. 程式人生 > >Python基礎知識兩部曲:二

Python基礎知識兩部曲:二

思想 你在 com 計算器 檢查 結果 條件 繼承 ddl

如果沒有看基礎部分第一章,請前往Python基礎知識兩部曲:一

8.函數

1.定義函數:

  1. 使用關鍵字def來告訴python你要定義一個函數
  2. 接著指出函數名:如下面函數名是--greet_user
  3. ()是必須帶上的,這裏可以可以傳遞一些參數,也可以不傳
  4. 以:結尾,且與後面所有的縮進構成了函數體
  5. 調用函數直接寫上函數名,如果有參數記得帶上參數
1. 無參數的函數:
def greet_user():
    """顯示簡單的函數體"""
    print("Hello Python")

greet_user()

得到:
Hello Python

2. 有參數的函數:

def greet_user(username):
    """顯示簡單的函數體"""
    print("Hello Python:  "+username)

greet_user(‘kobe‘)

得到:
Hello Python:  kobe
1.實參與形參

在函數greet_user()中,變量username是一個形參---函數完成其工作所需要的一項信息.在代碼greet_user(‘kobe‘)中,值‘kobe‘是一個實參。

2.傳遞實參

1.位置實參

需要註意參數的位置

def describe_pet(animal_type,pet_name):
    print("\nI have a " + animal_type + ".")
    print("My "+ animal_type + "‘s name is "+pet_name.title()+".")

describe_pet(‘dog‘,‘james‘)
describe_pet(‘dog‘,‘iverson‘)

得到:

I have a dog.
My dog‘s name is James.

I have a dog.
My dog‘s name is Iverson.
2.關鍵字實參

關鍵字實參是傳遞給函數的名稱-值對,直接在實參中將名稱和值關聯起來,因此向函數傳遞實參時不會混淆。與參數順序無關。

def describe_pet(animal_type,pet_name):
    print("\nI have a " + animal_type + ".")
    print("My "+ animal_type + "‘s name is "+pet_name.title()+".")

describe_pet(pet_name = ‘kkkk‘,animal_type = ‘cat‘)

得到:

I have a cat.
My cat‘s name is Kkkk.
3.默認值

編寫函數可以給每個形參指定默認值,

# 註意已經設置了默認值的參數要放在後面,
def describe_pet2(pet_name,animal_type = ‘dog‘):
    print("\nI have a " + animal_type + ".")
    print("My "+ animal_type + "‘s name is "+pet_name.title()+".")

describe_pet2(‘kobe‘)
describe_pet2(pet_name = ‘james‘)

得到:

I have a dog.
My dog‘s name is Kobe.

I have a dog.
My dog‘s name is James.

3.返回值

1.返回簡單值

調用返回值的函數時,需要提供一個變量,用於存儲返回的值。

def get_formatted_name(first_name,last_name):
    """返回整潔的姓名"""
    full_name = first_name + ‘-‘ +last_name
    return full_name.title()

musician = get_formatted_name(‘kobe‘,‘bryant‘)
print(musician)

得到:
Kobe-Bryant
2. 讓實參變成可選的

def get_formatted_name(first_name,last_name,middle_name= ‘‘):
    """返回整潔的姓名"""

    if middle_name:
        full_name = first_name +‘-‘+middle_name+‘-‘+last_name
    else:
        full_name = first_name + ‘-‘ +last_name

    return full_name.title()

musician = get_formatted_name(‘kobe‘,‘bryant‘)
print(musician)

musician = get_formatted_name(‘kobe‘,‘bryant‘,‘vboy‘)
print(musician)

得到:

Kobe-Bryant
Kobe-Vboy-Bryant
3. 返回字典
def build_person(first_name,last_name,age = ‘‘):
    """返回一個字典,其中包含有關一個人的信息"""
    person = {‘first‘:first_name,‘last‘:last_name}
    if age:
        person[‘age‘] = age
        pass

    return person

musician = build_person(‘kobe‘,‘bryant‘,23)
print(musician)

得到:
{‘age‘: 23, ‘last‘: ‘bryant‘, ‘first‘: ‘kobe‘}
4.結合使用函數和while循環
ef get_formatted_name(first_name,last_name,middle_name= ‘‘):
    """返回整潔的姓名"""

    if middle_name:
        full_name = first_name +‘-‘+middle_name+‘-‘+last_name
    else:
        full_name = first_name + ‘-‘ +last_name

    return full_name.title()

while True:
    print("\nPlease tell me you name:")
    first_name = input("first Name: ")
    last_name = input("last Name: ")
    formatted_name = get_formatted_name(first_name,last_name)
    print(formatted_name)

    msg = input("do you want to exit (Y/N)")
    if msg.upper() == ‘Y‘:
        break

終端運行得到:
liukingdeMacBook-Pro:desktop liuking$ python3 input.py

Please tell me you name:
first Name: kobe
last Name: bryant
Kobe-Bryant
do you want to exit (Y/N)n

Please tell me you name:
first Name: chris 
last Name: paul
Chris-Paul
do you want to exit (Y/N)y
liukingdeMacBook-Pro:desktop liuking$ 

4. 傳遞列表

1.在函數中修改列表
  • 沒有使用函數處理
# 沒有使用函數是這樣的。

"""將未確認的用戶,進行認證。"""
unconfirmed_users = [‘one‘,‘two‘,‘three‘]
confirmed_users = []

while unconfirmed_users:
    """處理用戶認證操作"""
    current_user = unconfirmed_users.pop()
    print("verifying User:"+current_user)
    confirmed_users.append(current_user)

"""打印認證用戶"""
print("\nThe following users have been confirmed: ")
for user in confirmed_users:
    print(user.title())

得到:
verifying User:three
verifying User:two
verifying User:one

The following users have been confirmed: 
Three
Two
One    
  • 使用函數處理
unconfirmed_users = [‘first‘,‘second‘,‘third‘]
confirmed_users = []

"""處理用戶認證操作"""
def deal_verify_user(unconfirmed_users,confirmed_users):
    while unconfirmed_users:
        """處理用戶認證操作"""
        current_user = unconfirmed_users.pop()
        print("verifying User:"+current_user)
        confirmed_users.append(current_user)

def print_verify_user(confirmed_users):
    for user in confirmed_users:
        print(user.title())

deal_verify_user(unconfirmed_users,confirmed_users)

print("\nThe following users have been confirmed: ")
print_verify_user(confirmed_users)

得到:
verifying User:third
verifying User:second
verifying User:first

The following users have been confirmed: 
Third
Second
First

上面我們發現得到一樣的結果,但使用了函數處理可以做到復用,且邏輯比較清晰,易於擴展。

2.禁止函數修改列表。

如果我們像備份之前的數據,我們就不能修改未認證的用戶,這個時候我們可以用切片來處理我們的操作了。

unconfirmed_users = [‘first‘,‘second‘,‘third‘]
confirmed_users = []

"""處理用戶認證操作"""
def deal_verify_user(unconfirmed_users,confirmed_users):
    while unconfirmed_users:
        """處理用戶認證操作"""
        current_user = unconfirmed_users.pop()
        print("verifying User:"+current_user)
        confirmed_users.append(current_user)

def print_user(confirmed_users):
    for user in confirmed_users:
        print(user.title())

"""這裏我們將列表的副本傳給函數,列表的原始數據不會被修改"""
deal_verify_user(unconfirmed_users[:],confirmed_users)

print("\nThe following users have been confirmed: ")
print_user(confirmed_users)

print("\n展示原始數據: ")
print_user(unconfirmed_users)

得到:
verifying User:third
verifying User:second
verifying User:first

The following users have been confirmed: 
Third
Second
First

展示原始數據: 
First
Second
Third

5.傳遞任意數量的實參。

有的時候我們不知道函數需要接受多少個實參,python允許函數從調用語句中收集任意數量的實參。

“”“這裏*toppons指定了一個空元組,將收到的所有值都封裝在這個這元組中。”“”
def make_pizza(*toppons):
    """打印顧客點的所有配料"""
    print("\nMaking a pizza with the following toppings")
    for top in toppons:
        print("- "+top.title())

make_pizza(‘pepperoni‘)
make_pizza(‘pepperoni‘,‘green peppers‘,‘extra cheese‘)

得到:

Making a pizza with the following toppings
- Pepperoni

Making a pizza with the following toppings
- Pepperoni
- Green Peppers
- Extra Cheese
1.結合使用位置實參和任意數量實參,

如果要讓函數接受不同類型的實參,必須在函數定義中接納任意數量實參的形參放在最後。python先匹配位置實參和關鍵字實參,再匹配任意實參,*所以這裏我們把make_pizza(size,toppons),位置實參在前,任意實參在後。**

def make_pizza(size,*toppons):
    """打印顧客點的所有配料"""
    print("\nMaking a " + str(size) +"-inch pizza with the following toppings")
    for top in toppons:
        print("- "+top.title())

make_pizza(18,‘pepperoni‘)
make_pizza(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)

得到:

Making a 18-inch pizza with the following toppings
- Pepperoni

Making a 33-inch pizza with the following toppings
- Pepperoni
- Green Peppers
- Extra Cheese
2.使用任意數量的關鍵字實參。

有時候需要接受任意數量的實參,但預先不知道傳遞給函數的會是什麽樣的信息。在這種情況下,可將函數編寫成能夠接受任意數量鍵值對---調用語句提供了多少就接愛多少。

  • 註意:使用任意數量的關鍵字實參需要使用**聲明
def build_profile(first,last,**user_info):
    """創建一個字典"""
    profile = {}
    profile[‘first_name‘] = first
    profile[‘last_name‘] = last

    for key,value in user_info.items():
        profile[key] = value

    return profile

info = build_profile(‘Kobe‘,‘bryant‘,like = ‘ball‘,age = 35)
print(info)

得到:
{‘first_name‘: ‘Kobe‘, ‘last_name‘: ‘bryant‘, ‘age‘: 35, ‘like‘: ‘ball‘}

6.將函數存儲在模塊中。

函數的優點之一是,使用它們可將代碼塊與主程序分離,通過給函數指定描述性名稱,可讓主程序容易得多。還可以更進一步,將函數存儲在被稱為模塊的獨立文件中,再將模塊導入到主程序中。import語句允許在當前運行的程序文件中使用模塊代碼。

通過將函數存儲在獨立的文件中,可隱藏程序代碼的細節,將重點入在程序的高層邏輯上,還能讓你在眾多不同的程序中重用函數。將函數存儲在獨立文件後,可與其它程序員共享這些文件而不是整個程序。知道如何導入函數,還能讓你使用其它程序員編寫的函數庫。

1.導入整個模塊。

使用import語句導入了名為module_name.py的整個模塊,就可使用下面的語法來使用其中任何一個函數。

module_name.function_name()<br/>

下面我們創建一個input.py文件。

def make_pizza(size,*toppons):
    """打印顧客點的所有配料"""
    print("\nMaking a " + str(size) +"-inch pizza with the following toppings")
    for top in toppons:
        print("- "+top.title())

再創建一個test_input.py文件

import input

input.make_pizza(18,‘pepperoni‘)
input.make_pizza(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)

得到:

Making a 18-inch pizza with the following toppings
- Pepperoni

Making a 33-inch pizza with the following toppings
- Pepperoni
- Green Peppers
- Extra Cheese

上面我們使用import 導入input文件。然後使用文件名input,再調用函數。

#####2.導入特定的函數。

  • 還可以導入模塊中特寫的函數,這種導入方法的語法如下:
    from module_name import function_name

  • 通過用逗號來分隔函數名,可根據需要從模塊中導入任意數量的函數:
    from module_name import function_0,function_1,function_2

對於只想導入要使用的函數,代碼將類似於下面這樣:

使用這種語法,調用函數時就無需使用句點,由於我們在import語句中顯示地導入了函數make_pizza,因此調用它時只需要指定其名稱。

from input import make_pizza

make_pizza(18,‘pepperoni‘)
make_pizza(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)

可以得到同樣的效果。
3.使用as給函數指定別名。

有的時候要導入的函數名稱可能與程序中現有的名稱沖突,或者函數名稱太長,可指定簡短而獨一無二的別名---函數的另一個名稱,類似於外號。

指定別名的通用語法是:
from module_name import function_name as fn

下面我們可以把上面代碼修改一下:

from input import make_pizza as mp

mp(18,‘pepperoni‘)
mp(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)
4.使用as給模塊指定別名

還可以給模塊指定別名。給模塊指定別名通用語法如下:
<br/>import module_name as mn<br/>
代碼如下:

import input as put

put.make_pizza(18,‘pepperoni‘)
put.make_pizza(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)
5.導入模塊中所有的函數

使用星號() 運算符可以讓Python導入模塊中所有的函數:
`from module_name import
`

首先創建有兩個函數的文件:

def make_pizza(size,*toppons):
    """打印顧客點的所有配料"""
    print("\nMaking a " + str(size) +"-inch pizza with the following toppings")
    for top in toppons:
        print("- "+top.title())

def make_KFC(size,*toppons):
    """打印顧客點的所有配料"""
    print("\nMaking a " + str(size) +"-inch KFC with the following toppings")
    for top in toppons:
        print("- "+top.title())

再調用:

from input import *

make_pizza(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)
make_KFC(33,‘pepperoni‘,‘green peppers‘,‘extra cheese‘)

得到:

Making a 33-inch pizza with the following toppings
- Pepperoni
- Green Peppers
- Extra Cheese

Making a 33-inch KFC with the following toppings
- Pepperoni
- Green Peppers
- Extra Cheese

註意:import語句中星號讓Python將模塊中每個函數都復制到這個程序文件中,由於導入了每個函數,可通過名稱來調用每個函數,而無需使用句點表示法。但使用並非自己編寫的大型模塊時,最好不要采用這種導入方法:如果模塊中有函數的名稱與你項目的中使用的名稱相同,可能導致意想不到的結果:Python可能遇到多個名稱相同的函數或變量,進而覆蓋函數,而不是分別導入所有的函數。

最佳做法:要麽只導入你需要使用的函數,要麽導入整個模塊並使用句點表示法。這能讓代碼更清晰,更容易理解和閱讀。

7.函數編寫指南

編寫函數時,需要牢記幾個細節:應給函數指定描述性名稱,且只在其中使用小寫字母和下劃線,描述性名稱可幫助你和別人明白代碼想要什麽,給模塊命名時也應按上述約定。
給形參指定默認值時,等號兩邊不要有空格。

def function_name(parameter_0,parameter_1=‘devault value‘)

對於函數調用中的關鍵字實參,

function_name(value_0,parameter=‘value‘)

9.類

所有的面向對象編輯思想都是一樣的,所以這一篇對於是程序員的你一定是非常簡單的.

9.1 創建和使用類

class Car():
    """一次模擬汽車的簡單嘗試"""

    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        long_name = str(self.year) + ‘ ‘ + self.make + ‘ ‘ + self.model
        return long_name.title()

    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("you can‘t roll back an odometer!")

這裏面我就創建了一個一個Car類,不要問我為什麽這麽寫,這就是約定。
代碼說明
def __init__(self, make, model, year):

  1. 這是一個特殊的函數,使用兩個下劃線標記主要是為了跟其它的普通函數區分開來。 在java裏這個叫構造函數
  2. 裏面有帶了幾個參數來填充屬性,還可以添加默認參數,裏面我添加了一個odometer_reading這個屬性
  3. 這裏面我添加了兩個方法get_descriptive_nameupdate_odometer 這裏面必須傳入self,這是對自身的一種引用,另外還可以在後面添加若幹參數。

使用類:

byd = Car(‘byd‘,‘byd tang‘,‘2017‘)        #實例化Car類
str1 = byd.get_descriptive_name()        # 調用類的方法
print(str1.title())

得到結果

2017 Byd Byd Tang

再調用一個帶參數的方法

byd.update_odometer(100);
print(‘move ‘+str(byd.odometer_reading)+‘ miles‘)

得到結果:
move 100 miles

9.2 繼承

直接在Car這個文件裏再寫一個子類,電動車類:

class ElectriCar(Car):            #繼承Car類
    """電動汽車獨特之處"""

    def __init__(self, make, model, year, battery_size=100):
        """初始化父類的屬性"""

        super().__init__(make, model, year)    #這裏繼承父類的屬性  和java裏的super方法一樣
        self.battery_size = battery_size        # 子類有自己的屬性

    def descript_batter(self):
        print("This car has a " + str(self.battery_size) + " kwh battery.")

    def fill_gas_tank(self):
        print("i hava a battery")

my_tesla = ElectriCar(‘tesla‘, ‘model s‘, ‘2016‘)

print(my_tesla.get_descriptive_name())  #引用父類的描述方法

print(my_tesla.fill_gas_tank())      #重寫子類的電池方法

得到結果:
2016 Tesla Model S
i hava a battery

代碼說明

  1. 在類名稱後的括號中寫上父類
  2. 在init方法中使用super方法來繼承父類的屬性
  3. 子類自動擁有父類全部的方法
  4. 子類可以重寫父類方法,但方法名一定要寫父類一樣.

####9.3 導入類

####9.4 Python標準庫

10.文件和異常

3.異常

異常是使用try-except代碼塊處理的。try-except代碼塊讓Python執行指定的操作,同時告訴Python發生異常時怎麽辦。使用了try-except代碼塊時,即便出現異常,程序也將繼續運行:顯示你編寫的友好的錯誤信息,而不是令用戶迷惑的traceback.

1.處理ZeroDivisionError異常。
print(5/0)
2.使用try-except代碼塊

當你認為可能發生了錯誤時,可編寫一個try-except代碼塊來處理可能引發的異常。

處理ZeroDivisionError異常的try-except代碼塊類似於下面這樣:

try:
    print(5/0)
except Exception, e:
    print("You can‘t divide by zero!")

如果程序出現異常,就執行print("You can‘t divide by zero!"),不再是traceback:

3.使用異常避免崩潰

發生錯誤時,如果程序還有工作沒有完成,妥善處理錯誤就尤其重要。這種情況經常會現出現在要求用戶提供輸入的程序中;如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入,而不至於崩潰。

下面來一個只執行除法的簡單計算器:


print("Give me two numbers, and I‘ll divide them.")
print("Enter ‘q‘ to quit. ")
while True:
    first_number = input("\nFirst number: ")
    if first_number == ‘q‘:
        break;

    second_number = input("\nSecond number: ")
    if second_number == ‘q‘:
        break;

    answer = int(first_number)/int(second_number)
    print(answer)

得到:
liukingdeMacBook-Pro:desktop liuking$ python3 input.py
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit. 

First number: 4

Second number: 2
2.0

First number: 4

Second number: g
Traceback (most recent call last):
  File "input.py", line 244, in <module>
    answer = int(first_number)/int(second_number)
ValueError: invalid literal for int() with base 10: ‘g‘
liukingdeMacBook-Pro:desktop liuking$ 
4. else代碼塊。

通過將可能引發錯誤的代碼放在try-except代碼塊中,可提高這個程序抵禦錯誤的能力,錯誤是是執行除法運算的代碼行導致的,因此我們需要將它放到try-except代碼塊中。依賴於try代碼塊成功執行的代碼都放到else代碼塊中:


print("Give me two numbers, and I‘ll divide them.")
print("Enter ‘q‘ to quit. ")
while True:
    first_number = input("\nFirst number: ")
    if first_number == ‘q‘:
        break;

    second_number = input("\nSecond number: ")
    if second_number == ‘q‘:
        break;

    try:
        answer = int(first_number)/int(second_number)
    except Exception:
        print("you can‘t divide by 0!")
    else:
        print(answer)

得到:
liukingdeMacBook-Pro:desktop liuking$ python3 input.py
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit. 

First number: 5

Second number: 3
1.6666666666666667

First number: 5

Second number: 0
you can‘t divide by 0!

First number: 

發現異常也能友好的提示給用戶。

try-except-else代碼塊的工作原理大致如下:python嘗試執行try代碼塊中的代碼;只有可能引發異常的代碼才需要放在try語句中。有時候,有一些僅在try代碼塊成功執行時才需要運行的代碼,這些代碼應該放在else代碼塊中。except代碼塊告訴python,如果它嘗試運行try代碼塊中的代碼時引發了指定的異常,該怎麽辦。

通過預測可能發生錯誤的代碼,可編寫健壯的程序,它們即便面臨無效數據或者缺少資源,也能繼續運行,從而能夠抵禦無意的用戶錯誤和惡意的攻擊。

5. 處理FileNotFoundError異常。

4.存儲數據

一般都是使用模塊json來存儲數據。

1.使用json.dump()寫入數據和json.load()加載數據。

使用json.dump()來存儲(寫入)數據

import json
numbers = [2,3,5,7,9,22,44]
file_name = ‘numbers.json‘
with open(file_name,‘w‘) as f_obj:
    json.dump(numbers,f_obj)

我們先要導入模塊json,再執行,最後可以打開numbers.json文件,看到其內容與python中一樣。

再使用json.load()讀取numbers.json文件:

import json

file_name = ‘numbers.json‘
with open(file_name) as f_obj:
    numbers = json.load(f_obj)

print(numbers)

得到:
[2, 3, 5, 7, 9, 22, 44]

與我們期望的一致。

11.測試代碼

1.測試

1.單元測試和測試用例

Python標準庫中的模塊unitest提供了代碼測試工具。單元測試用於測試函數的某個方面是否有問題;測試用例是一組單元測試,這些單元測試一起核實函數在各種情形下的行為都符合要求。

#####2.可通過的測試
要為函數編寫測試用例,可先導入模塊unittest以及要測試的函數,再創建一個繼承unittest.TestCase的類,並編寫一系列方法對函數行為的不同方面進行測試。

首先我們來寫一個方法:

def get_formatted_name(first,last):
    """Generate a neatly formatted full name."""
    full_name = first + ‘ ‘ + last
    return full_name.title()

再寫一個測試用例

import unittest
from name_function import get_formatted_name

class NameTestCase(unittest.TestCase):
    """測試name_function.py"""

    def test_first_last_name(self):
        """能夠正確地處理Janis Joplin這樣的姓名嗎?"""
        formatted_name = get_formatted_name(‘janis‘,‘joplin‘)
        self.assertEqual(formatted_name,‘Janis Joplin‘)

unittest.main()

得到:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
  • 首先我們導入了模塊unittest和要測試的函數get_formatted_name()
  • 我們創建了一個NameTestCase的類用於包含一系列針對get_formatted_name()的單元測試。可以隨便給這個類命名,但最好讓它看起來要與測試的函數相關,並包含字樣Test。這個類必須繼承unittest.TestCase類
  • 我們使用了unittest類最有用的功能之一:一個斷言方法。斷言方法用來核實得到的結果是否與期望的結果一致。
  • 第1行的句點表明有一個測試通過了,接下來的一行指出Python運行了一個測試,消耗的時候不到0.01s,最後的OK表明該測試用例中的所有單元測試都通過了。
  • 測試方法名為test-first-last-name(),方法名必須以test_開頭,這樣它才會在我們測試的時候自動運行。這個方法名清楚地指出了它測試的是get_formatted_name()的那個行為,這樣如果該測試未通過,我們就會馬上知道受影響的是那種類型的姓名。在TestCase類中使用很長的方法名是可以的,這些方法的名稱必須是描述性的這才能讓你明白測試未通過的時的輸出,這些方法由python自動調用,你根本不用編寫調用它們的代碼。
3.不能通過的測試

這裏我們給出一個不能通過測試的案例

def get_formatted_name(first,middle,last):
    """Generate a neatly formatted full name."""
    full_name = first + ‘ ‘ + middle + ‘ ‘ + last
    return full_name.title()

再運行一下:

E
======================================================================
ERROR: test_first_last_name (__main__.NameTestCase)
能夠正確地處理Janis Joplin這樣的姓名嗎?
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/liuking/Desktop/test_name_function.py", line 11, in test_first_last_name
    formatted_name = get_formatted_name(‘janis‘,‘joplin‘)
TypeError: get_formatted_name() takes exactly 3 arguments (2 given)

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
[Finished in 0.1s with exit code 1]
  • 首先指出測試用例中有一個單元測試導致了錯誤
  • NameTestCase中的test_first_last_name()導致了錯誤,知道那個測試沒有通過至關重要。
  • 我們看到了Traceback
4. 測試未通過時怎麽辦

測試未通過時怎麽辦?如果檢查的條件沒錯,測試通過了意味著函數的行為是對的,而測試未通過意味著你編寫的新代碼有錯,因此測試未通過時,不要修改測試,而應修復導致測試不能通過的代碼:檢查剛對函數所做的修改,找到導致函數行為不符合預期的修改。

把剛才的函數代碼稍作修改:

def get_formatted_name(first,last,middle = ‘‘):
    """Generate a neatly formatted full name."""
    if middle:
        full_name = first + ‘ ‘ + middle + ‘ ‘ + last
    else:
        full_name = first + ‘ ‘ + last

    return full_name.title()

得到:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[Finished in 0.1s]

又能正確通過測試。

5.添加新測試

我們為NamesTestCase再添加一個方法:

# -*- coding: utf8 -*-

import unittest
from name_function import get_formatted_name

class NameTestCase(unittest.TestCase):
    """測試name_function.py"""

    def test_first_last_name(self):
        """能夠正確地處理Janis Joplin這樣的姓名嗎?"""
        formatted_name = get_formatted_name(‘janis‘,‘joplin‘)
        self.assertEqual(formatted_name,‘Janis Joplin‘)

    def test_first_last_middle_name(self):
        """能夠正確地處理Janis Joplin Kobe這樣的姓名嗎?"""
        formatted_name = get_formatted_name(‘janis‘,‘Kobe‘,‘joplin‘)
        self.assertEqual(formatted_name,‘Janis Joplin Kobe‘)

unittest.main()

得到:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
[Finished in 0.1s]

2.測試類

1.各種斷言方法

常用的斷言方法,使用這些方法可核實返回的值等於或不等於預期的值,返回的值為True或False,返回的值在列表中或者不在列表中。只能在繼承unittest.TestCase的類中使用這些方法

unittest Module中的斷言方法

方法 用途
assertEqual(a,b) 核實 a ==b
assertNotEqual(a,b) 核實 a !=b
assertTrue(x) 核實x為True
assertFalse(x) 核實x為False
assertIn(item,list) 核實item在list中
assertNotIn(item,list) 核實item不在list中
2.一個要測試的類

類的測試與函數的測試相似--你所做的大部分工作都是測試類中方法的行為,但存在一些不同之處,

# -*- coding: utf8 -*-

class AnonymousSurvey():
    """收集匿名調查問卷的答案"""
    def __init__(self, question):
        self.question = question
        self.responses = []

    def show_question(self):
        """顯示調查問卷"""
        print(self.question)

    def store_response(self,new_response):
        """存儲單份調查問卷"""
        self.responses.append(new_response);

    def show_results(self):
        """顯示收集到的所有答案"""
        print("Survey Results:")
        for response in self.responses:
            print(‘- ‘+response)

    ------------------------------------------------------------------------------------------

    from survey import AnonymousSurvey

#定義一人問題,並創建一個表示調查的AnonymousSurvey對象
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)

my_survey.show_question()
print("Enter ‘q‘ at any time to quit.\n")
while True:
    response = input("language: ")
    if response == ‘q‘:
        break
    my_survey.store_response(response)

#顯示調查結果:
print("\nThank you to everyone who participated in the survey?")
my_survey.show_results()

運行得到:
在終端運行得到:
What language did you first learn to speak?
Enter ‘q‘ at any time to quit.

language: english
language: chinese
language: japanese
language: q

Thank you to everyone who participated in the survey?
Survey Results:
- english
- chinese
- japanese
3.測試AnonymousSurvey類

下面來編寫一個測試,對AnonymousSurvey類的行為的一個方面進行驗證:如果用戶面對調查問題時只提供一個答案,這個答案也能被妥善保存,為此我們將在這個答案被保存後,用方法assertIn()來核實包含在答案列表中:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """docstring for ClassName"""
    def test_store_single_response(self):
        question = "what language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        my_survey.store_response(‘english‘)

        self.assertIn(‘english‘,my_survey.responses)

unittest.main()

運行得到:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[Finished in 0.1s]

這裏我們首先導入了模塊unittest以及要測試的類AnonymousSurvey,它也繼承於unittest.TestCase第一個測試方法驗證調查問題的單個答案被存儲後,會包含在調查結果列表中。

只能收集一個答案的調查用途不大,我們來核實用戶提供的三個答案,也將它們存儲。

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """docstring for ClassName"""
    def test_store_single_response(self):
        question = "what language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        my_survey.store_response(‘english‘)

        self.assertIn(‘english‘,my_survey.responses)

    def test_store_three_responses(self):
        question = "what language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        responses = [‘english‘,‘chinese‘,‘japanese‘]
        for response in responses:
            my_survey.store_response(response)

        for response in responses:
            self.assertIn(response,my_survey.responses)

unittest.main()

運行得到:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
[Finished in 0.1s]
4. 方法setUp()

在unittest.TestCase類包含方法setUp(),讓我們只需要創建這些對象一次,並在每個測試方法中使用他們,如果你在TestCase類中包含了方法setUp(),Python將先運行它,再運行各個以test_打頭的方法,這樣在我們編寫的每個測試方法中都可使用方法setUp()中創建的對象。

# -*- coding:utf8 -*-

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):

    def setUp(self):
        """創建一個調查對象和一組答案,供使用的測試方法使用。"""
        question = "What language did you first learn to speak?"
        self.my_survey = AnonymousSurvey(question)
        self.responses = [‘chinese‘,‘english‘,‘japanese‘]

    """docstring for ClassName"""
    def test_store_single_response(self):
        self.my_survey.store_response(self.responses[0])
        self.assertIn(self.responses[0],self.my_survey.responses)

    def test_store_three_responses(self):

        for response in self.responses:
            self.my_survey.store_response(response)

        for response in self.responses:
            self.assertIn(response,self.my_survey.responses)

unittest.main()

運行得到:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
[Finished in 0.1s]

方法setUp()讓測試方法編寫起來更容易,可在setUp()方法中創建一系列並設置他們的屬性,再在測試方法中直接使用這些實例,相比於在每個測試方法中都都創建並設置其屬性,這要容易得多。

Python基礎知識兩部曲:二