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é)尾坡脐,且與后面所有的縮進構(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.默認值

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

# 注意已經(jīng)設(shè)置了默認值的參數(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ù)是這樣的护戳。

"""將未確認的用戶践磅,進行認證。"""
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    
  • 使用函數(shù)處理
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

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

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

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

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

"""這里我們將列表的副本傳給函數(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ù)量的實參,但預先不知道傳遞給函數(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ù)指定描述性名稱过椎,可讓主程序容易得多室梅。還可以更進一步,將函數(shù)存儲在被稱為模塊的獨立文件中疚宇,再將模塊導入到主程序中亡鼠。import語句允許在當前運行的程序文件中使用模塊代碼。

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

1.導入整個模塊迅矛。

使用import語句導入了名為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 導入input文件。然后使用文件名input威兜,再調(diào)用函數(shù)销斟。

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

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

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

使用這種語法逮栅,調(diào)用函數(shù)時就無需使用句點悴势,由于我們在import語句中顯示地導入了函數(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ù)指定別名。

有的時候要導入的函數(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.導入模塊中所有的函數(shù)

使用星號(*) 運算符可以讓Python導入模塊中所有的函數(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ù)都復制到這個程序文件中,由于導入了每個函數(shù)提前,可通過名稱來調(diào)用每個函數(shù)吗货,而無需使用句點表示法。但使用并非自己編寫的大型模塊時狈网,最好不要采用這種導入方法:如果模塊中有函數(shù)的名稱與你項目的中使用的名稱相同宙搬,可能導致意想不到的結(jié)果:Python可能遇到多個名稱相同的函數(shù)或變量,進而覆蓋函數(shù)拓哺,而不是分別導入所有的函數(shù)勇垛。

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

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

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

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ù),使用兩個下劃線標記主要是為了跟其它的普通函數(shù)區(qū)分開來姑食。 在java里這個叫構(gòu)造函數(shù)
  2. 里面有帶了幾個參數(shù)來填充屬性波岛,還可以添加默認參數(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 導入類

9.4 Python標準庫

10.文件和異常

3.異常

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

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

2.使用try-except代碼塊

當你認為可能發(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í)行除法運算的代碼行導致的妖异,因此我們需要將它放到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ā)了指定的異常,該怎么辦蟀俊。

通過預測可能發(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)
    

我們先要導入模塊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標準庫中的模塊unitest提供了代碼測試工具。單元測試用于測試函數(shù)的某個方面是否有問題瞧筛;測試用例是一組單元測試厉熟,這些單元測試一起核實函數(shù)在各種情形下的行為都符合要求。

2.可通過的測試

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

首先我們來寫一個方法:

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和要測試的函數(shù)get_formatted_name()
  • 我們創(chuàng)建了一個NameTestCase的類用于包含一系列針對get_formatted_name()的單元測試〉呵恚可以隨便給這個類命名底循,但最好讓它看起來要與測試的函數(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]

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

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

把剛才的函數(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.各種斷言方法

常用的斷言方法,使用這些方法可核實返回的值等于或不等于預期的值榨汤,返回的值為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類的行為的一個方面進行驗證:如果用戶面對調(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]

這里我們首先導入了模塊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)系作者
  • 序言:七十年代末败去,一起剝皮案震驚了整個濱河市仆救,隨后出現(xiàn)的幾起案子胰锌,更是在濱河造成了極大的恐慌秃症,老刑警劉巖陨献,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寸士,死亡現(xiàn)場離奇詭異谜诫,居然都是意外死亡慕趴,警方通過查閱死者的電腦和手機痪蝇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冕房,“玉大人躏啰,你說我怎么就攤上這事“也幔” “怎么了丙唧?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長觅玻。 經(jīng)常有香客問我想际,道長,這世上最難降的妖魔是什么溪厘? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任胡本,我火速辦了婚禮,結(jié)果婚禮上畸悬,老公的妹妹穿的比我還像新娘侧甫。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布披粟。 她就那樣靜靜地躺著咒锻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪守屉。 梳的紋絲不亂的頭發(fā)上惑艇,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音拇泛,去河邊找鬼滨巴。 笑死,一個胖子當著我的面吹牛俺叭,可吹牛的內(nèi)容都是我干的恭取。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼熄守,長吁一口氣:“原來是場噩夢啊……” “哼蜈垮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起裕照,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤窃款,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后牍氛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡烟阐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年搬俊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜒茄。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡唉擂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出檀葛,到底是詐尸還是另有隱情玩祟,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布屿聋,位于F島的核電站空扎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏润讥。R本人自食惡果不足惜转锈,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望楚殿。 院中可真熱鬧撮慨,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至规伐,卻和暖如春蟹倾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背楷力。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工喊式, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萧朝。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓岔留,卻偏偏與公主長得像,于是被迫代替她去往敵國和親检柬。 傳聞我的和親對象是個殘疾皇子献联,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

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