1.变量和简单数据类型

1)字符串

  • xx.tilte()以首字母大写的方式显示每个单词

  • xx.upper()大写

  • xx.lower()小写

  • f"hello,{xx1}xx{xx2}"在输出字符串重输出变量

  • xx.rstrip()删除右边的空白

  • xx.lstrip()删除左边的空白

  • xx.strip()删除两边的空白

2)数

只要操作数有一个是浮点数,得到的默认总是浮点数

书写很大的数时,可以使用下划线将其中的数字分组,print时不会打印其中的下划线

可以逗号给变量连续赋值,如a,b,c=0,1,2

2.列表

1)简介

students=["chenyanheng","chenzikun"]
print(students)
#-->["chenyanheng","chenzikun"]

print(students[0])
#-->chenyanheng

print(f"众所周知{students[0].title}是一个大帅比")
#-->众所周知Chenyanheng是一个大帅比

索引从0开始,要输出最后一个元素可以用xx[-1],倒数第二个xx[-2]类推

2)修改、增删元素

students=["chenyanheng","chenzikun"]

  • students[0]="shuaibi"修改元素

  • students.append(“chenshuaibi”)在列表末尾添加元素

  • students.insert(0,“shuai”)在索引0处添加空间,并将“shuai”储存到这个地方,将其他元素都右移一个位置

  • del students[0]删除索引为0处的元素

  • xx=students.pop()返回列表末尾的元素并后将其删除

  • xx=students.pop(1)弹出列表索引为1的元素并删除

  • students.remove(“shuai”)删除列表中一个为”shuai“值的元素

3)组织列表

  • students.sort()按字母顺序(升序)永久地修改列表元素的排列顺序

  • stusents.sort(reverse=True)按字母顺序(降序)永久地修改列表元素的排列顺序

  • print(sorted(students,reverse=False)临时修改列表元素的排列顺序

  • students.reverse()反转列表元素排列顺序

  • len(students)获得列表长度

4)避免索引错误

3.操作列表

1)遍历整个列表

for student in students:

print(student)

有缩进的代码都会重复执行

2)避免缩进错误

expected an indented block缺少缩进块

unexpected indent不必要的缩进

3)创建数值列表

for value in range(1,5):

print(value)

输出1到4(因为函数range()让python 从指定的第一个值开始数,到达指定的第二个值时结束,所以在第二个值时停止没有输出)

若range()参数列表里只有一个数,则从0到这个数-1

可以用函数list()将range()的结果转换为列表如numbers=list(range(1,6))

print(numbers)

使用range()可以在第三个参数设定步长,如range(1,10,3)=[1,4,7]

min(numbers)返回列表中的最小值

max(numbers)返回列表中的最大值

sum(numbers)返回列表数字的和

squares=[value**2 for value in range(1,11)]称为列表解析,可以直接将1到10每个数的平方放到列表中

==for value in range(1,11):

squares.append(value**2)

4)使用列表的一部分

创建切片:返回第一个索引指定的元素的到最后一个索引之前的元素

  • print(numbers[0:3])返回0,1,2号索引对应的元素形成的列表

  • [1:4]1,2,3号索引对应的元素

  • [:3]从表头到2号索引对应的元素

  • [3:]从3号索引对应的元素到表尾

  • [-3:]倒数三个索引对应的元素

student_0=students_1会使得两个列表关联,而不是将后者的副本赋给前者,当修改其中一个时,另一个也会随之改变

要实现列表赋值给另一个列表,应该使用student_0=student_1[:]

5)元组

不可以改变的列表称之为元组

元组用括号定义dimensions=(200,150)

严格地说,元组是由逗号标识的,圆括号只是为了看起来更加的整洁,如果定义一个只包含一个元素的元组,必须在这个元素后面加上逗号如my_t=(50,)

不能修改元组的元素,但可以给存储元组的变量赋值,如dimensions=(200,50)是正确的,dimensions[1]=50是错的

6)设置代码格式

缩进:四个空格符

行长:代码79个字符,注释72个字符

4.If语句

1)条件测试

一般判断是否相等时区分大小写car="Audi",if(car=="audi")返回false

如果大小写无关紧要可以car="Audi",if(car.lower()=="audi")则会返回true

if(xx!=xxx):

xxxxxxxxxx

检查不相等

  • 多个条件都满足:and

  • 多个条件满足一个:or

  • 判断值是否在列表里:in

  • 判断值是否不在列表里:not in

2)if语句

if xxxx:
	xxxxx
elif xxx:
	xxxx
else:
    xxxx

3)使用if语句处理列表

if(students):#列表students里至少有一个元素
	for student in students:
		print(student)
	print("All Sign Up")
else:#列表里没有元素
	print("No Student")

4)设置if语句的格式

==,>=,<=,>,<左右加空格

5.字典

1)使用字典

字典是一系列键值对,每个键都与一个值相关联,可以使用键来访问相关联的值,用花括号表示一系列键值对

alien_0={

"color":green,

"point":5,

}

  • alien_0[color]访问字典中的值

  • alien_0[“x_position”]=0

  • alien_0[“y_position”]=25添加键值对

  • alien_0[“color”]=red修改字典中的值

  • del alien_0[color]删除键值对

字典中元素的排列顺序与添加顺序相同

alien_0.get(“point”,“No point value assigned”)第一个参数用于指定键,第二个参数为指定的键不存在时返回的值(默认返回None)

2)遍历字典

for key,value in alien_0.**item()**:#遍历所有键值对
	print(f"Key:{key}")
	print(f"Value:{value}")
  • for key in alien_0.keys():遍历所有键(也可以省略keys(),仍会自动遍历所有键如for key in alien_0:输出结果相同)

    keys()并非只能用于遍历,实际上它将返回一个列表,其中包含列表里的所有键。

  • for name in sorted(favorate_languages.keys()):按指定顺序遍历字典中的所有键

  • for value in alien_0.values():遍历所有值,没有考虑重复

    为剔除重复项,可以使用集合set,集合中的每个元素都是独一无二的。

  • for value in set(alien_0.value()):

    languages={“python”,“c”,“python",c++"}利用花括号直接创建集合(无键值对)

    print(languages)

    –>{“c”“python”,“c++”}集合不同于列表和字典,不会以特定的顺序存储元素

3)嵌套

字典列表:

alien_0={"color":"green","point":"5"}

alien_1={"color":"red","point":"5"};

alien_2={"color":"white","point":"5"}

aliens=[alien_0,alien_1,alien_2]

创建三十个白色五号大小的机器人存储进alien字典:

for alien in range(30):

new_alien={"color":"white","point":"5"}

aliens.append(new_alien)

修改前三个机器人的颜色:

for alien in aliens[:3]:

alien["color"]="yellow"

在字典中存储列表:

字典中的某个键对应的值是由一个列表组成

favorite_languages={

"jen":["python","c++"],

"sarah":["c"],

"phil":["python","go","ruby],

}

for name,languages in favorite_languages.items():

print(f"\n{name.title()}'s favorite languages are:")

for language in languages:

print("\t"+language)

#在字典里存储字典
users = {
   "aeinstein": {
      "first": "albert",
      "last": "einstein",
      "location": "princeton",
   },
   "mcurie": {
      "first": "marie",
      "last": "curie",
      "location": "paris",
   },
}

6.用户输入和while循环

1)函数input()的工作原理

函数input()让程序暂停运行,等待用户输入一些文本,其接受一个参数——要向用户显示的提示或说明

显示的提示或说明若有两行及以上:

prompt = "If you tell us who you are,we can personalize the messages you see"
prompt += "\nWhat is your first name?"

可以使用int()来获取数值输入:

age=input("How old are you?")
age = int(age)
if age == 18:
   print("Yes!")

只有化为int后才可以比较大小。

2)while循环简介

循环读入消息直到遇到"quit":

prompt = "Tell me something!\nYou can enter 'quit' to end the program."
message = ""
while message != "quit":
   message = input(prompt)
   if message != "quit":
        print(message)

使用标志判断是否结束循环:

active = True
message = ""
while active:
   message = input("Input here:")
   if message == "quit":
      active = 0;
   else:
      print(message)

使用break可以跳出循环。

使用continue可以结束本次循环,进入下次循环。

3)使用while循环处理列表和字典

删除为特定值的所有列表元素:

pets = ["dog", "cat", "dog", "goldfish", "cat", "rabbit", "cat"]
while 'cat' in pets:
   pets.remove("cat")
print(pets)

7.函数

1)定义函数

def greet_user():
   """显示简单的问候语。"""
   print("Hello!")
greet_user()

第二行处的文本是称为文档字符串的注释,描述了函数是做什么的,用三引号括起,Python使用它们来生成有关程序中函数的文档。

2)传递实参

向函数传递实参的方式很多:可以使用位置实参,这要求实参的顺序与形参的顺序相同;

也可以使用关键字实参,期中每个实参都由变量名和值组成;

还可以使用列表和字典。

def describe_pet(animal_type, pet_name):
   """显示宠物信息"""
   print(f"\nI have a {animal_type}.")
   print(f"My {animal_type}'s name is {pet_name.title()}.")


"""位置实参,关联方式基于实参的顺序"""
describe_pet("hamster", "harry")
"""关键字实参,关联方式与位置顺序无关"""
describe_pet(pet_name="harry", animal_type="hamster")

带有默认值的函数:

def describe_pet(pet_name, animal_type="dog"):
   print(f"\nI have a {animal_type}.")
   print(f"My {animal_type}'s name is {pet_name.title()}.")

在此函数定义中,必须交换形参的顺序,把带默认值的放在后面,否则编译失败。因为调用时输入的参数会按顺序赋给形参,如果放在前面则会把实参赋给带默认值的形参,而没带默认值的形参缺乏参数。

3)返回值

让实参变成可选:让可选择是否填写的实参放在参数列表最末位,如果是字符串,则赋值为"",如果是数值则赋值为None

然后函数定义中使用if语句,如

def get_formatted_name(first_name,last_name,middle_name=""):
   if middle_name:
   else:

可以返回字典和列表

4)传递列表

传递列表后再函数中修改列表会导致列表被改变,为了保留改变前的列表,可以禁止函数修改列表,

做法为:传递参数时将列表的副本传递给函数,如function_name(list_name[:])

5)传递任意数值的实参

有时候预先不知道函数需要接受多少个实参,则可以创建一个空元组,并将收到的所有值封装到这个元组,

def make_pizza(*toppings):
   """打印顾客点的所有配料"""
   for topping in toppings:
      print(f"-{topping}")


make_pizza("pepperoni")
make_pizza("mushrooms", "green peppers", "extra cheese")

要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。

Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。

通用形参名L:*args

使用任意数量的关键字实参:

有时候需要接受任意数量的实参但预先不知道传递给函数的会是什么样的信息,在这种情况下可将函数编写成能够接受任意数量的键值对。

def build_profile(first, last, **user_info):
   """创建一个字典,其中包含我们知道的有关用户的一切信息"""
   user_info['first_name'] = first
   user_info['last_name'] = last
   return user_info


user_profile = build_profile('albert', 'einstein', location='princeton', filed='physics')
print(user_profile)

形参名**kwargs,用于收集任意数量的关键字形参

6)将函数存在模块中

可以将函数存储在称为模块的独立文件中,再将模块导入到主程序中。

import语句允许在当前运行的程序文件中使用模块中的代码。

导入整个模块:

模块是扩展名为.py的文件。创建完模块后在该文件所在目录中创建主程序,在主程序中调用该模块。

如:创建名为pizza.py的文件,后在主程序中使用代码import pizza,则可以使用该模块中的所有函数。

代码行import pizza让Python打开文件pizza.py,并将其中所有函数都复制到整个程序中(看不见)。

调用函数时:pizza.make_pizza(),即模块名.函数名

导入特定的函数:

from module_name import function_name(function0,function1,function2…)

from pizza import make_pizza

使用as给函数指定别名:

如果导入函数名称可能与程序中现有的名称冲突或者函数名称太长,可指定简短而独一无二的别名:

函数的另一个名称,类似与外号。要给函数取这种特殊外号,需要在导入它时指定。

from pizza import make_pizza as mp

mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')

使用as给模块指定别名:

import pizza as p

p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

导入模块中的所有函数:

from pizza import *,使用该语句引入函数后,调用时不需要通过句点表示法(模块名.函数名),可直接使用函数名调用,

然而因此名称相同时容易出现覆盖函数,一般不使用这种方式,

7)函数编写指南

给形参指定默认值时,等号两边不要有空格;

对于函数调用中的关键字实参,左右两边也不需要有空格;

函数命名时只是用小写字母和下划线,给函数指定描述性名称;

每个函数都应该包含简要的阐述其功能的注释,该注释应紧跟在函数定义后面;

如果形参很多导致函数定义的长度超过了79个字符,可在函数定义中输入左括号后按回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体区分开。如:

def function_name(
        xxx, xxxx, xxxxx,
        x, xxxxxx
):
    function body...

在程序或模块中包含多个函数,可使用两个空行将相邻的函数分开;

所有import语句都应放在文件开头,除非在文件开头使用了注释来描述整个程序。

8.类

1)创建和使用类

class Dog:
    """一次模拟小狗的简单尝试"""
    def __init__(self, name, age):
        """初始化属性name和age"""
        self.name = name
        self.age = age

    def sit(self):
        """模拟小狗收到命令时蹲下"""
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        """模拟小狗收到命令时打滚"""
        print(f"{self.name} rolled over!")

类中的函数称为方法。

创建实例:my_dog = Dog("Willie", 6)

访问属性:my_dog.name

调用方法:my_dog.sit()

类名首字母大写,方法名小写

2)使用类和实例

修改属性的值:

直接修改属性的值:my_new_car.odometer_reading()

通过方法修改属性的值:

class Car:
    --snip--
    
    def uodate_odometer(self, mileage):
        """
        将里程表读数设置为指定的值
        禁止将里程表读数回调
        """
        if(mileage >= self.odometer_reading()):
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

3)继承

创建ElectricCar类的一个简单版本,它具备Car类的所有功能:

class ElectricCar(Car):
    """电动汽车的独特之处"""
    def __init__(self, make, model, year):
        """
        初始化父类的属性
        再初始化电动汽车特有的属性
        """
        super.__init__(make, model, year)
        self.battery_size = 75
        
        
ma_tesla = ElectricCar("tesla","model s",2019)
print(ma_tesla.get_descriptive_name())#调用父类的函数

创建子类时父类必须包含在当前文件中,且位于子类前面。

super是一个特殊函数,让你能够调用父类的方法。

在子类可以定义一个与要重写的父类方法同名的方法,这样即可覆盖父类的方法。

将实例用作属性:

当给类添加的细节越来越多时,属性和方法清单以及文件都越来越长,这是可以将类的一部分提取出来,作为一个独立的类。可以将大类型拆分成多个协同的小类。

class Battery:
    """一次模拟电动汽车电瓶的简单尝试"""
    def __init__(self, battery_size=75):
        """初始化电瓶的属性"""
        self.battery_size=battery_size

    def describe_battery(self):
        """打印一条描述电瓶容量的消息"""
        print(f"This car has a {self.battery_size}-kwh battery.")


class ElectricCar(Car):
    """电动汽车的独特之处"""
    def __init__(self, make, model, year):
        """
        初始化父类的属性
        再初始化电动汽车特有的属性
        """
        super.__init__(make, model, year)
        self.battery = Battery()#这行代码让Python创建了一个新的Battery实例(因为没有指定容量故为默认值75)
        

my_tesla = ElectricCar("tesla", "model s'", 2019)
my_tesla.battery.describe_battery()

在ElectricCar类中添加了一个名为self.battery的属性,并将创建的新的Battery实例赋给属性self.battery

4)导入类

from car import Car该语句让Python打开模块car.py并导入其中的Car类

from car import Car, ElectricCar从一个模块中导入多个类

import car 导入整个模块,访问类时用句点法(模块名.类名)

from car import *导入模块中所有类(不推荐使用,从模块中导入很多类时推荐导入整个模块,因为导入整个模块调用时需要带模块名,不会产生名称冲突)

from electric_car import ElectricCar as EC给导入的类指定一个别名,使用时可直接使用这个别名

5)Python标准库

Python标准库是一组模块,安装的Python中都包含。

模块random:

randint():将两个整数作为参数,并随机返回一个位于这两个整数之间(含)的整数。

choice():将一个列表或元组作为参数,并随机返回其中的一个元素。

6)类编码风格

驼峰命名法:

类名中的每个单词的首字母都大写,不使用下划线

实例名和模块名都采用小写格式,每个单词之间加上下划线

对于每个类,都应紧跟在类定义后包含一个文档字符串来简要地描述该类的功能

每个模块也应该包含一个文档字符串来对其中的类可用于做什么进行描述

在类中可以用一个空行来分割方法

在模块中可以使用两个空行来分割类

需要同时导入标准库中的模块和自己编写的模块时,先编写导入标准库模块的import语句,再添加一个空行,然后编写导入你自己编写的模块的import语句

9.文件和异常

1)从文件中读取数据

with open('pi_digits.txt') as file_object:

contents = file_object.read()

要以任何方式使用文件,哪怕只是打印内容,都需要打开文件才能访问。

函数open()接一个参数,即要打开的文件的名称。Python将在当前执行的文件所在的目录中查找指定的文件。

open()打开文件后不需要关闭,系统会自动在合适时机将其关闭。自行调用close可能会在存在bug时导致close()未执行而没有关闭文件。未妥善关闭文件可能导致数据丢失或受损。

使用语句print(contents)后输出结果的末尾多了一个空行(如果鼠标指针在新启一行)。这是因为read()达到文件末尾时放回一个空字符串。要删除空行可以调用print(contents.rstrip())

open()参数中显示文件路径时一般使用斜杆(虽然Windows系统使用反斜杠),因为反斜杠会被识别为转义字符,当然也可以对每个反斜杠进行转义。如'text_files/filename.txt''text_files\\filename.txt'(此二者为相对路径)

绝对路径:'C:\\path\\to\\file.txt'

"""逐行读取"""
filename = 'pi_digits.txt'

with open(filename) as file_object:
    for line in file_object:
        print(line.rstrip())

读取文本文件时,Python将其中的所有文本都解读为字符串。如果要将其作为数值使用,就必须使用函数int()转化为整数或使用float()转化为浮点数。

""""判断生日是否在Π中"""

filename = 'pi_digits.txt'
pi_string = ''

with open(filename) as file_object:
    lines = file_object.readlines()
for line in lines:
    """将几行数字合为一个字符串"""
    pi_string += line.strip()

birthday = input("Enter your birthday,in the form mmddyy:")
if birthday in pi_string:
    print("Your birthday appears in the digits.")
else:
    print("your birthday does not appear in the digits.")

2)写入文件

filename = 'pi_digits1.txt'

with open(filename,'w') as file_object:
    file_object.write("Hello World!")

open()第二个实参为模式实参:

  • ‘w’:以写入模式打开;

  • ‘r’:以读取模式打开;

  • ‘a’:以附加模式打开;(给文件添加内容而不是覆盖原有的内容)

  • ‘r+’:以读写模式打开;

如果省略了模式实参,Python将以默认的只读模式打开文件。

如果写入的文件不存在将自动创建,如果指定的文件已经存在,将清空该文件的内容

Python只能将字符串写入文本文件,要将数值数据存储到文本文件中,必须先使用函数str()使其转化为字符串格式。

函数write()不会在写入的文本末尾添加换行符,因此如果写入多行时没有指定换行符会导致两行连接在一起。

‘a’会将内容添加到文件末尾,同样不会有换行符。

3)异常

每当发生让Python不知所措的错误是,他都会创建一个一异常对象。如果你编写了处理该异常的代码,程序将继续运行,如果未对异常进行处理,程序将停止并显示traceback,其中包含有关异常的报告。

"""处理ZeroDivisionError异常"""
try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")
else:
	print(answer)

将导致错误的代码行放在一个try代码块中。如果try代码块中的代码运行起来没有问题,Python将跳过except代码块;如果导致了错误将查找与之匹配的except代码块并运行其中的代码。如果try-except代码块后面还有其他代码,程序会接着运行。

有时候一些代码尽在try代码块成功执行时才需要运行,这些代码应放在else代码块中。(尝试某些代码,出现某些指定异常则执行对应except代码块,没有异常则运行else代码块)

FileNotFoundError异常:Python找不到要打开的文件时创建的异常。这个错误是函数open()导致的,因此要处理这个错误,必须将tr语句放在包含open()的代码行之前。

title = "Alice in Wonderland in"
print(title.split())

输出结果为['Alice', 'in', 'Wonderland', 'in']

def count_word(name):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(name, encoding='utf-8') as f:
            contents = f.read()
    except FileNotFoundError:
        print(f"Sorry,the file {name} does not exist.")
    else:
        words = contents.split()#split可以根据一个字符串创建一个单词列表。 
        num_words = len(words)
        print(f"The file {name} has about {num_words} words.")


filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
    count_word(filename)

静默失败:捕获到异常后像什么都没有发生一样继续运行。只需要把except代码块的代码换成pass:

except FileNotFoundError:

pass

4)存储数据

用户关闭程序时。总是要保存他们提供的信息,一种简单的方是使用模块json(JavaScript Object Notation)来存储数据。模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。

import json

numbers = [2, 3, 5, 7, 11, 13]

filename = 'numbers.json'
with open(filename, 'w') as f:
    json.dump(numbers,f)

json.dump()接受两个实参:要存储的数据,以及可用于存储数据的文件对象。数据的存储格式与Python中一样。

import json

filename = 'numbers.json'
with open(filename) as f:
    numbers = json.load(f)

print(numbers)

json.load()加载存储在其实参中的信息。

import json

# 如果以前存储了用户名,就加载它。
# 否则,提示用户输入用户名并存储它。
filename = 'username.json'
try:
    with open(filename) as f:
        username = json.load(f)
except FileNotFoundError:
    username = input("What is your name?")
    with open(filename, 'w') as f:
        json.dump(username, f)
        print(f"We will remember you when you come back,{username}!")
else:
    print(f"Welcome back,{username}!")

上述代码可进行重构,而得到更清晰且易于维护和扩展的代码,如下:

import json


def get_stored_username():
    """如果存储了用户名,就获取它"""
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None
    else:
        return username


def get_new_username():
    """提示用户输入用户名"""
    username = input("What is your name?")
    filename = 'username.json'
    with open(filename, 'w') as f:
        json.dump(username, f)
    return username


def greet_user():
    """"问候用户,并指出其名字"""
    username = get_new_username()
    if username:
        print(f"Welcome back,{username}!")
    else:
        username = get_new_username()
        print(f"We will remember you when you come back,{username}!")
        

greet_user()

10.测试代码

1)测试函数

Python提供了了一种自动测试函数输出的高效方式(不再需要每次修改代码后自行输入数据测试),由标准库中的模块unittest提供。

单元测试用于核实函数的某个方面没有问题。测试用例是一组单元测试,它们一道核实在各种情形下的行为全部符合要求。良好的测试用例考虑了函数可能收到的各种输入,包含针对所有这些情形的测试。

全覆盖的测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。

"""name_function"""
def get_formatted_name(first, last, middle=""):
    """生成姓名"""
    if middle:
        full_name = f"{first} {middle} {last}"
    else:
        full_name = f"{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')

    def test_first_last_middle_name(self):
        """能够正确处理像Wolfgang Amadeus Mozart这样的姓名吗"""
        formatted_name = get_formatted_name('wolfgang', 'mozart', 'amadeus')
        self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')


if __name__ == '__main__':
    unittest.main()

运行该程序时,所有以test_打头的方法都将自行运行。

创建一个类,用于包含一系列针对要测试函数的单元测试。

这个类可以随便命名(最好以Test起始),但必须继承unittest.TextCase类,这样Python才知道如何运行你编写的测试。

self.assertEqual(formatted_name, 'Janis Joplin')是unittest类最有用的功能之一:断言方法。

断言方法核实得到的结果是否与期望的结果一致。意思是将formatted_name的值与字符串’Janis Joplin’进行比较。

很多测试框架都会先导入测试文件再运行,导入文件时,解释器将在导入的同时执行它。

if代码块检查特殊变量__name__,这个变量时在程序执行时设置的。如果这个文件为主程序执行时,该变量将被设置为__main__。在这里,调用unittest.main()来运行测试用例,如果这个文件被测试框架导入,变量__name__的值将不是__main__,因此不会调用unittest.main()

若输出结果为OK则表示该测试用例中的单元测试都通过了,未通过测试则返回FAILED。

2)测试类

六个常用的断言方法(只能在继承unittest.TestCase的类中使用这些方法)


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

"""survey"""
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 responses:
            print(f"-{response}")
import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
    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_response(self):
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        responses = ['English', 'Spanish', 'Mandarin']
        for response in responses:
            my_survey.store_response(response)
        for response in responses:
            self.assertIn(response, my_survey.responses)


if __name__ == '__main__':
    unittest.main()

在该测试中,每个测试方法都创建了一个AnonymousSurvey的实例,并在每个方法中都创建了答案。

然而调用unittest.TestCase中的方法setUp()可以让我们只需要创建这些对象一次,就可以在每个测试方法中使用。

如果在测试方法中包含了方法setUpI(),Python将先运行它,再运行每个以test_打头的方法,这样在每个测试方法中都可以使用在方法setUp()中创建的对象。如下:

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 = ['English', 'Spanish', 'Mandarin']

    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_response(self):
        for response in self.responses:
            self.my_survey.store_response(response)
        for response in self.responses:
            self.assertIn(response, self.my_survey.responses)


if __name__ == '__main__':
    unittest.main()

方法setUp()创建了一个调查对象以及一个答案列表,存储这两样东西的变量名包含了前缀self(即存储在属性中),因此可在这个类的任何地方使用。

测试自己编写的类时,方法setUp()让测试方法编写起来更容易:可在setUp()方法中创建一系列实例并设置其属性,再在测试方法中直接使用这些实例。相比与在每个测试方法中都创建实例并设置其属性要容易得多。

Q.E.D.


   七岁几胆敢预言自己,操一艘战机