Python基礎(chǔ)教程二

主要內(nèi)容: 1.函數(shù) 2.類 3.異常處理 4.文件

8.函數(shù)

1.定義函數(shù):

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

得到:
Hello Python

2. 有參數(shù)的函數(shù):

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

得到:
Hello Python:  kobe

1.實參與形參

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

2.傳遞實參

1.位置實參

需要注意參數(shù)的位置

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.關(guān)鍵字實參

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

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.默認(rèn)值

編寫函數(shù)可以給每個形參指定默認(rèn)值,

# 注意已經(jīng)設(shè)置了默認(rèn)值的參數(shù)要放在后面,
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.返回簡單值

調(diào)用返回值的函數(shù)時,需要提供一個變量,用于存儲返回的值。

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 = ''):
    """返回一個字典,其中包含有關(guān)一個人的信息"""
    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.結(jié)合使用函數(shù)和while循環(huán)
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.在函數(shù)中修改列表
  • 沒有使用函數(shù)處理
# 沒有使用函數(shù)是這樣的。

"""將未確認(rèn)的用戶,進(jìn)行認(rèn)證。"""
unconfirmed_users = ['one','two','three']
confirmed_users = []

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

"""打印認(rèn)證用戶"""
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    
  • 使用函數(shù)處理
unconfirmed_users = ['first','second','third']
confirmed_users = []

"""處理用戶認(rèn)證操作"""
def deal_verify_user(unconfirmed_users,confirmed_users):
    while unconfirmed_users:
        """處理用戶認(rèn)證操作"""
        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

上面我們發(fā)現(xiàn)得到一樣的結(jié)果,但使用了函數(shù)處理可以做到復(fù)用,且邏輯比較清晰,易于擴展。

2.禁止函數(shù)修改列表。

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

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

"""處理用戶認(rèn)證操作"""
def deal_verify_user(unconfirmed_users,confirmed_users):
    while unconfirmed_users:
        """處理用戶認(rèn)證操作"""
        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())

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

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

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

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

The following users have been confirmed: 
Third
Second
First

展示原始數(shù)據(jù): 
First
Second
Third

5.傳遞任意數(shù)量的實參。

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

“”“這里*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.結(jié)合使用位置實參和任意數(shù)量實參,

如果要讓函數(shù)接受不同類型的實參,必須在函數(shù)定義中接納任意數(shù)量實參的形參放在最后。python先匹配位置實參和關(guān)鍵字實參,再匹配任意實參,所以這里我們把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.使用任意數(shù)量的關(guān)鍵字實參。

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

  • 注意:使用任意數(shù)量的關(guān)鍵字實參需要使用**聲明
def build_profile(first,last,**user_info):
    """創(chuàng)建一個字典"""
    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.將函數(shù)存儲在模塊中。

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

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

1.導(dǎo)入整個模塊。

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

module_name.function_name()

下面我們創(chuàng)建一個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())
        

再創(chuàng)建一個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 導(dǎo)入input文件。然后使用文件名input,再調(diào)用函數(shù)。

2.導(dǎo)入特定的函數(shù)。
  • 還可以導(dǎo)入模塊中特寫的函數(shù),這種導(dǎo)入方法的語法如下:
    from module_name import function_name

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

對于只想導(dǎo)入要使用的函數(shù),代碼將類似于下面這樣:

使用這種語法,調(diào)用函數(shù)時就無需使用句點,由于我們在import語句中顯示地導(dǎo)入了函數(shù)make_pizza,因此調(diào)用它時只需要指定其名稱。

from input import make_pizza

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

可以得到同樣的效果。

3.使用as給函數(shù)指定別名。

有的時候要導(dǎo)入的函數(shù)名稱可能與程序中現(xiàn)有的名稱沖突,或者函數(shù)名稱太長,可指定簡短而獨一無二的別名---函數(shù)的另一個名稱,類似于外號。

指定別名的通用語法是:
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給模塊指定別名

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

import input as put

put.make_pizza(18,'pepperoni')
put.make_pizza(33,'pepperoni','green peppers','extra cheese')

5.導(dǎo)入模塊中所有的函數(shù)

使用星號(*) 運算符可以讓Python導(dǎo)入模塊中所有的函數(shù):
from module_name import *

首先創(chuàng)建有兩個函數(shù)的文件:

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())
        

再調(diào)用:

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將模塊中每個函數(shù)都復(fù)制到這個程序文件中,由于導(dǎo)入了每個函數(shù),可通過名稱來調(diào)用每個函數(shù),而無需使用句點表示法。但使用并非自己編寫的大型模塊時,最好不要采用這種導(dǎo)入方法:如果模塊中有函數(shù)的名稱與你項目的中使用的名稱相同,可能導(dǎo)致意想不到的結(jié)果:Python可能遇到多個名稱相同的函數(shù)或變量,進(jìn)而覆蓋函數(shù),而不是分別導(dǎo)入所有的函數(shù)。

最佳做法:要么只導(dǎo)入你需要使用的函數(shù),要么導(dǎo)入整個模塊并使用句點表示法。這能讓代碼更清晰,更容易理解和閱讀。

7.函數(shù)編寫指南

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

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

對于函數(shù)調(diào)用中的關(guān)鍵字實參,

function_name(value_0,parameter='value')

9.類

所有的面向?qū)ο缶庉嬎枷攵际且粯拥?,所以這一篇對于是程序員的你一定是非常簡單的.

9.1 創(chuàng)建和使用類

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!")

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

  1. 這是一個特殊的函數(shù),使用兩個下劃線標(biāo)記主要是為了跟其它的普通函數(shù)區(qū)分開來。 在java里這個叫構(gòu)造函數(shù)
  2. 里面有帶了幾個參數(shù)來填充屬性,還可以添加默認(rèn)參數(shù),里面我添加了一個odometer_reading這個屬性
  3. 這里面我添加了兩個方法get_descriptive_nameupdate_odometer 這里面必須傳入self,這是對自身的一種引用,另外還可以在后面添加若干參數(shù)。

使用類:

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

得到結(jié)果

2017 Byd Byd Tang

再調(diào)用一個帶參數(shù)的方法

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

得到結(jié)果:
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())      #重寫子類的電池方法

得到結(jié)果:
2016 Tesla Model S
i hava a battery


代碼說明

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

9.3 導(dǎo)入類

9.4 Python標(biāo)準(zhǔn)庫

10.文件和異常

3.異常

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

1.處理ZeroDivisionError異常。
print(5/0)

2.使用try-except代碼塊

當(dāng)你認(rèn)為可能發(fā)生了錯誤時,可編寫一個try-except代碼塊來處理可能引發(fā)的異常。

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

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

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

3.使用異常避免崩潰

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

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


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代碼塊。

通過將可能引發(fā)錯誤的代碼放在try-except代碼塊中,可提高這個程序抵御錯誤的能力,錯誤是是執(zhí)行除法運算的代碼行導(dǎo)致的,因此我們需要將它放到try-except代碼塊中。依賴于try代碼塊成功執(zhí)行的代碼都放到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: 

發(fā)現(xiàn)異常也能友好的提示給用戶。

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

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

5. 處理FileNotFoundError異常。

4.存儲數(shù)據(jù)

一般都是使用模塊json來存儲數(shù)據(jù)。

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

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

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)
    

我們先要導(dǎo)入模塊json,再執(zhí)行,最后可以打開numbers.json文件,看到其內(nèi)容與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標(biāo)準(zhǔn)庫中的模塊unitest提供了代碼測試工具。單元測試用于測試函數(shù)的某個方面是否有問題;測試用例是一組單元測試,這些單元測試一起核實函數(shù)在各種情形下的行為都符合要求。

2.可通過的測試

要為函數(shù)編寫測試用例,可先導(dǎo)入模塊unittest以及要測試的函數(shù),再創(chuàng)建一個繼承unittest.TestCase的類,并編寫一系列方法對函數(shù)行為的不同方面進(jìn)行測試。

首先我們來寫一個方法:

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

  • 首先指出測試用例中有一個單元測試導(dǎo)致了錯誤
  • NameTestCase中的test_first_last_name()導(dǎo)致了錯誤,知道那個測試沒有通過至關(guān)重要。
  • 我們看到了Traceback
4. 測試未通過時怎么辦

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

把剛才的函數(shù)代碼稍作修改:

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.添加新測試

我們?yōu)镹amesTestCase再添加一個方法:

# -*- 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.各種斷言方法

常用的斷言方法,使用這些方法可核實返回的值等于或不等于預(yù)期的值,返回的值為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.一個要測試的類

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

# -*- coding: utf8 -*-

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

    def show_question(self):
        """顯示調(diào)查問卷"""
        print(self.question)
        
    def store_response(self,new_response):
        """存儲單份調(diào)查問卷"""
        self.responses.append(new_response);

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

#定義一人問題,并創(chuàng)建一個表示調(diào)查的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)

#顯示調(diào)查結(jié)果:
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類的行為的一個方面進(jìn)行驗證:如果用戶面對調(diào)查問題時只提供一個答案,這個答案也能被妥善保存,為此我們將在這個答案被保存后,用方法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]

這里我們首先導(dǎo)入了模塊unittest以及要測試的類AnonymousSurvey,它也繼承于unittest.TestCase第一個測試方法驗證調(diào)查問題的單個答案被存儲后,會包含在調(diào)查結(jié)果列表中。

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

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(),讓我們只需要創(chuàng)建這些對象一次,并在每個測試方法中使用他們,如果你在TestCase類中包含了方法setUp(),Python將先運行它,再運行各個以test_打頭的方法,這樣在我們編寫的每個測試方法中都可使用方法setUp()中創(chuàng)建的對象。

# -*- coding:utf8 -*-

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):


    def setUp(self):
        """創(chuàng)建一個調(diào)查對象和一組答案,供使用的測試方法使用。"""
        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()方法中創(chuàng)建一系列并設(shè)置他們的屬性,再在測試方法中直接使用這些實例,相比于在每個測試方法中都都創(chuàng)建并設(shè)置其屬性,這要容易得多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 不知道大家是否還記得Python有一個優(yōu)點是跨平臺呢?正因為Python是跨平臺的,所以它可以運行在Windows...
    獨身犬閱讀 715評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • 突然間的感覺,不知道自己到底進(jìn)了哪裡,不喜歡商界不喜歡爭鬥,可是身邊的人都那麼心機深重,層層疊疊的讓人難以駕馭...
    宋小朝閱讀 125評論 0 1
  • 鐵打的營盤,流水的兵,流水的兵……又到了退伍季,整個軍營都彌漫著離別的悲傷。一首首傷感的軍歌,觸動著每一位鐵血戰(zhàn)士...
    越努力越幸運_32e5閱讀 416評論 1 2

友情鏈接更多精彩內(nèi)容