fenghzy 发表于 2018-8-10 07:32:27

Python基础知识两部曲:二

  如果没有看基础部分第一章,请前往Python基础知识两部曲:一

8.函数

1.定义函数:


[*]使用关键字def来告诉python你要定义一个函数
[*]接着指出函数名:如下面函数名是--greet_user
[*]()是必须带上的,这里可以可以传递一些参数,也可以不传
[*]以:结尾,且与后面所有的缩进构成了函数体
[*]调用函数直接写上函数名,如果有参数记得带上参数
  

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 = 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):



[*]这是一个特殊的函数,使用两个下划线标记主要是为了跟其它的普通函数区分开来。 在java里这个叫构造函数
[*]里面有带了几个参数来填充属性,还可以添加默认参数,里面我添加了一个odometer_reading这个属性
[*]这里面我添加了两个方法get_descriptive_name 和 update_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
  

  

  代码说明


[*]在类名称后的括号中写上父类
[*]在init方法中使用super方法来继承父类的属性
[*]子类自动拥有父类全部的方法
[*]子类可以重写父类方法,但方法名一定要写父类一样.
  ####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(&quot;You can't divide by zero!&quot;),不再是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 =
  
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)
  

  
得到:
  

  

  

  与我们期望的一致。

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)
  

  

  


[*]首先指出测试用例中有一个单元测试导致了错误
[*]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
  

  

  

  又能正确通过测试。

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
  

  

  

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

  

  

  这里我们首先导入了模块unittest以及要测试的类AnonymousSurvey,它也继承于unittest.TestCase第一个测试方法验证调查问题的单个答案被存储后,会包含在调查结果列表中。
  只能收集一个答案的调查用途不大,我们来核实用户提供的三个答案,也将它们存储。
  

import unittest  
from survey import AnonymousSurvey
  

  
class TestAnonymousSurvey(unittest.TestCase):

  """docstring for>  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
  

  

  

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>  def test_store_single_response(self):
  self.my_survey.store_response(self.responses)
  self.assertIn(self.responses,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
  

  

  

  方法setUp()让测试方法编写起来更容易,可在setUp()方法中创建一系列并设置他们的属性,再在测试方法中直接使用这些实例,相比于在每个测试方法中都都创建并设置其属性,这要容易得多。
页: [1]
查看完整版本: Python基础知识两部曲:二