0%

Python基础

Python的安装、IPython的使用

  • Python的安装参见https://stringpiggy.hpd.io/mac-osx-python3-dual-install/ ,或者《Python编程:从入门到实战》一书中也有详细的记载。
  • 利用IPython可以代替传统python开发的文本编辑器,灵活的改变所写代码,快捷的生成结果代码。IPython Notebook目前已经成为用Python做教学、计算、科研的一个重要工具,很多示例都是用.ipynb文件。

​ 安装Ipython有两种方式,第一种是通过pip安装。第二种是安装Anaconda从而获得iPython (这个选择比较适合新手,推荐官网教程一键搞定),下面主要介绍使用pip命令来安装。

  1. iPython是由很多模块组成的, 为了不漏装任何组件,我用了这个命令来安装所有组件:
1
$ sudo pip install ipython[all]
  1. 安装成功后通过这命令来运行Notebook
1
$ ipython notebook

之后有可能会弹出这个错误(你如果选择了用Anaconda的方式来安装也会碰到这个错误):

1
ValueError, 'unknown locale: %s' % localename

StackOverflow上已经有人提出了解决方案,在命令行里找到.bash_profile 然后添加下面两行代码:

1
2
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

然后别忘了重新载入.bash_profile让新添加的代码生效(注意两个点之间的空格):

1
$ . .bash_profile

之后再运行命令”ipython notebook “就可以在浏览器里看到iPython Notebook的界面了。

iPython Notebook的工作原理是在本地启动一个服务器,你通过localhost:8888/tree 这个地址就可以连接到这个服务器上与之通信。从而实现在浏览器里写代码,传给本地服务器执行,然后本地服务器传回结果并在网页上呈现这个循环。

每次使用完Notebook,只关闭网页本身是不够的。需要在你启动Notebook的那个Terminal里输 Ctrl + c 然后确认,才能把服务器关闭。官网具体链接为:http://IPython.org/install.html

变量和数据类型

变量

变量由字母、数字、下划线组成,但是必须以字母或下划线开头,并避免大写。

1
2
3
>>>message = 'Hello Python world!'
>>>print(message)
Hello Python world!

字符串

python中,用引号括起来的就是字符串,不论是单引号还是双引号,但是必须成对,不可混用。。

1
2
3
4
5
6
7
8
>>> 5+8
13
>>> '5+8'
'5+8'
>>> '5'+'8'
'58'
>>> 'let\'s go'
"let's go"

修改字符串的大小写

  • title():以首字母大写的方式显示每个单词
1
2
3
1 name = "ada love alce"
2 print(name.title())
Ada Love Alce
  • upper():将字符串全部改为大写
1
2
3
1 name = "ada love alce"
2 print(name.upper())
ADA LOVE ALCE
  • lower():将字符串全部改为小写
1
2
3
1 name = "ada love alce"
2 print(name.lower())
ada love alce

合并字符串

python中使用 “+” 来合并字符串

1
2
3
4
5
6
7
8
1 first_name = "ada"
2 last_name = "alace"
3 full_name = first_name + " " + last_name

4 message = "Hello," + full_name.title() + "!"
5 print(message)

Hello,Ada Alace!

制表符与换行符

制表符“\t”、换行符“\n ”

1
2
3
4
5
1 print("language:\n\tPython\n\tC\n\tJavaScript")
language:
Python
C
JavaScript

删除空白

找出字符串开头和末尾多余的空白。去末尾空白rstrip(),开头lstrip()、 开头和末尾strip()

1
2
3
4
5
6
7
8
9
10
11
12
1 favourite_language = " python "
2 favourite_language.rstrip()
' python'

1 favourite_language.lstrip()
'python '

1 favourite_language.strip()
'python'

1 favourite_language
' python '

但是当输出favourite_language的值时,发现并没有改变。要永久删除这个字符串中的空白,必须将删除结果存回到变量中。

1
2
3
4
1 favourite_language =" python "
2 favourite_language = favourite_language.strip()
3 favourite_language
'python'

使用字符串时避免语法错误

例如,当使用单引号标明一个字符串时,如果字符串中间出现了撇号,那么python解释器可能无法识别。这时将单引号改成双引号即可。

1
2
3
4
5
6
1 message = 'One of Python's strengths is its diverse community' message
2 message
File "<ipython-input-24-e0db9437bcf6>", line 1
message = 'One of Python's strengths is its diverse community' message
^
SyntaxError: invalid syntax
1
2
3
1 message = "One of Python's strengths is its diverse community" 
2 message
"One of Python's strengths is its diverse community"

原始字符串

原始字符串,只需在字符串前加上一个‘r’即可(用来处理字符串中若出现多个转义字符的情况)

1
2
3
4
5
6
7
8
9
10
>>> str = 'C:\note'
>>> print(str)
C:
ote
>>> str = 'C:\\note'
>>> print(str)
C:\note
>>> str = r'C:\note\noat\rnate'
>>> print(str)
C:\note\noat\rnate

数字

整数

(+)(-)()(/)、(*)代表乘方运算

1
2
1 3 ** 3
27

浮点数

1
2
>>> 1.5e11
150000000000.0

类型转换与比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> a = "520"   #Transfer,str->int
>>> b = int(a)
>>> print(b)
520

>>> age = 23 #Transfer,int->str
>>> message = "Happy " + str(age) +"rd Birthday!"
>>> message
'Happy 23rd Birthday!'

>>> a = "520" #比较类型
>>> type(a)
<class 'str'>
>>> a = "小"
>>> isinstance(a,str)
True

注释

用“#”标明。

Python常用操作符

  • 算术运算符: “+、 -、、 /、 +=、-=、*、//、%”
1
3 ** 2   //3^2
  • 比较操作符:“>、<、>=、<=”
  • 逻辑运算符:not > and > or
1
3<4<5   //等同于(3<4and (4<5)
  • 优先级问题

    幂运算>正负号>算数操作符>比较操作符>逻辑操作符

Python之禅

Python程序员笃信代码可以编写得漂亮而优雅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1 import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

列表

基本操作

列表存储一系列元素,Python中,用“[]” 来表示列表,并用“,”来分隔其中的元素。

1
2
3
1 bicycle = ['trek','cannodale','redline','specialized']
2 print(bicycle)
['trek', 'cannodale', 'redline', 'specialized']

访问列表元素

指出该元素的索引即可。

1
2
1 print(bicycle[0])
trek

应该灵活的与字符串内容结合起来。

1
2
3
4
1 names = ['jones','lace','grace']
2 for each in names: #“:”不可少
3 print(each.title(),end=' ') #此处需要缩进
Jones Lace Grace

索引从“0”开始,值得注意的是,当要访问最后一个元素,可以指定索引为“-1”,索引-2返回倒数第二个列表元素,以此类推。

添加元素

  • 在列表末尾添加新元素:append()
1
1 names.append('Benedict')
  • 在列表中插入元素:insert()
1
1 names.insert(0,"Angela")

删除元素

  • 使用del语句删除元素(知道位置),删除后无法继续访问
1
1 del names[0]
  • 使用方法pop()删除元素,弹出后可以继续访问。
1
2
3
4
5
6
7
1 poped_person = names.pop()   #无参弹出末尾元素
2 poped_person
Benedict

1 poped_person = names.pop(0) #带参弹出指定元素
2 poped_person
lace
  • 根据值删除元素remove()
1
2
3
4
5
1 names
['grace', 'benedict', 'lace', 'jones', 'grace', 'benedict', 'lace', 'jones']
names.remove('jones')
names
['grace', 'benedict', 'lace', 'grace', 'benedict', 'lace', 'jones']

可以看到,remove()只删除第一个’jones’。 删除所有,使用while

组织列表

  • 使用sort()方法对列表进行永久性排序
1
2
3
4
5
6
7
1 cars = ['baoma','zoka','dazhong']
2 cars.sort(); #正序
3 print(cars)
['baoma', 'dazhong', 'zoka']
4 cars.sort(reverse=True)
5 cars
['zoka', 'dazhong', 'baoma'] #倒序
  • 使用sorted()方法对列表进行临时排序
1
2
3
4
5
6
7
1 print(sorted(cars))		#注意写法
2 cars
['baoma', 'dazhong', 'zoka']
['zoka', 'dazhong', 'baoma']

3 print(sorted(cars,reverse = True)) #也可指定顺序
['zoka', 'dazhong', 'baoma']
  • 倒序函数reverse()
1
2
3
1 cars.reverse()
2 cars
['zoka', 'dazhong', 'baoma']
  • 列表长度len()
1
2
1 len(cars)
3

操作列表

for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1 magicians = ['alace','brown','danny','li']
2 for magician in magicians: #冒号
3 print("magician:"+magician+"your trick is so amazing!\n")
4 print("I can't wait to see your next")
5 print("Thank u,everyone.That was a great magic show!")

magician:alaceyour trick is so amazing!

I can't wait to see your next
magician:brownyour trick is so amazing!

I can't wait to see your next
magician:dannyyour trick is so amazing!

I can't wait to see your next
magician:liyour trick is so amazing!

I can't wait to see your next
Thank u,everyone.That was a great magic show!

注意:

  1. Python根据缩进来判断代码行与前一个代码行的关系。

  2. 在for循环的后面,每个缩进的代码行都是循环的一部分,没有缩进的代码都只执行一次。

  3. for语句末尾的冒号告诉Python,下一行是循环的第一行。

创建数值列表:range()

1
2
3
4
5
6
1 for value in range(1,5):
2 print(value)
1
2
3
4

差一行为,所以并不会打印数字“5”。

要创建数字列表:

  1. 可使用list()range()的结果直接转换为列表。
1
2
1 numbers = list(range(1,6))
2 numbers
  1. 使用循环,利用列表函数
1
2
3
4
5
6
1 squares = []     #输出1`10的平方
2 for value in range(1,11):
3 square = value**2
4 squares.append(square)
5 print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

range()还可以设置步长,按指定步长增长。

1
2
3
1 even_numbers = list(range(2,11,2))   #设置步长为2,输出2~11之间的偶数
2 print(even_numbers)
[2, 4, 6, 8, 10]

对数字列表进行简单的统计计算

min()、max()、sum()

1
2
3
4
5
6
7
1 digits = [1,2,3,4,5,6,7]
2 min(digits)
1
3 max(digits)
7
4 sum(digits)
28

列表解析

更加简洁的创建列表的方式。

1
2
3
1 squares = [value**2 for value in range(1,11)]
2 print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

使用列表的一部分—-列表切片

1
2
3
1 players = ['charles','martina','florence']
2 print(players[0:3]) #第一个元素到第三个元素,输出下标0~2的元素
['charles', 'martina', 'florence']

与range()一样,python在达到第二个数字“3”后停止,所以只输出三个元素。

负数索引返回离列表末尾相应距离的元素,因此可以输出列表末尾的任何切片。

1
1 print(players[-3:])    #打印最后三名队员的名字

遍历切片

1
2
1 for player in players[0:3]:
2 print(player.title()) #打印前三名队员的名字

复制列表

1
2
3
4
5
6
7
8
1 my_foods = ['pizza','falafel','carrot']
2 friend_foods = my_foods[:] #这里不等同于friend_foods = my_foods
3 friend_foods
['pizza', 'falafel', 'carrot']

1 friend_foods = my_foods[0:1]
2 friend_foods
['pizza']

注:friend_foods = my_foods[:] 与 friend_foods = my_foods的区别:

friend_foods = my_foods[:],是将my_foods的副本存储到friend_foods 中,因此会产生两个列表。

而friend_foods = my_foods,是将新变量friend_foods关联到my_foods列表,因此这两个列表都指向同一个列表

元组

列表非常适合用于存储在程序运行期间可能变化的数据集。但有时你需要创建一系列不可修改的元素。不可变的列表被称为元组。

定义元组

元组采用圆括号而不是方括号来标识。

1
2
3
4
5
1 dimensions = (200,50)   #定义
2 print(dimensions[0]) #访问
3 print(dimensions[1])
200
50

:Python规定不能给元组的元素赋值。因此类似于

1
1 dimension[0] = 30

是禁止的。

遍历元组

同列表语法

1
2
1 for dimension in dimensions:
2 print(dimension)

修改元组变量

虽然不能修改元组的元素,但可以给存储元组的变量赋值。因此,若需要修改dimensions,可重新定义整个元组

1
1 dimensions = (400,100) #重新定义

元组的删除是python自动执行的。无需自动删除。

设置代码格式

PEP 8官网

  1. 缩进:PEP 8建议每级缩进都使用4个空格。(或者设置制表符为4个空格)

  2. 行长:建议每行不超过80字符。

  3. 空行:使用空行将程序的不同部分分开。

if语句

基本语法

  • 一个条件
1
2
3
4
5
6
1 cars = ['audi','benchi','bmw','haha']
2 for car in cars:
3 if car == 'bmw': #注意这里字符串比较直接用’==‘ 不相等用“!=”
4 print(car.upper())
5 else:
6 print(car.title())

注意”:“的使用,注意缩行。并且,python比较相等时区分大小写

  • 多个条件: “and””or”
1
2
3
4
1 age0 = 0
2 age1 = 1
3 age0 >= 21 and age1 >= 21
FALSE
  • 检查特定值是否包含在列表中 “in、not in”
1
2
3
1 request_toppings = ['mushrooms','onions','pineapple',]
2 'mushrooms' in request_toppings
TRUE

if-elif-else 与 if-if-if

1
2
3
4
5
6
7
8
9
1 age = 12
2 if age < 4:
3 price = 0
4 elif age < 18:
5 price = 5
6 else:
7 price = 10
8 print("Your admission cost is $" + str(price) +".")
Your admission cost is $5.

总之,如果你只想执行一个代码块,就使用if-else-if 结构;如果要运行多个代码块,就使用一系列独立的if语句。

确定列表是否为空

1
2
3
4
5
6
7
1 requested_toppings = []
2 if requested_toppings:
3 for requested_topping in requested_toppings:
4 print("Adding "+ requested_topping + ".")
5 else: #缩进位置一定要正确,否则运行错误。
6 print("Are u sure u want to a plain pizza?")
Are u sure u want to a plain pizza?

注:在if语句中将列表名用在条件表达式中时,Python将在列表至少包含一个元素时返回True,并在列表为空时返回False。

字典

将相关信息联系起来的字典。

一个简单的字典

1
2
3
4
5
1 alien_0 = {'color':'green','points':5}
2 print(alien_0['color'])
3 print(alien_1['points'])
green
5

使用字典

在Python中,字典时一系列”键-值“对。每个键都与一个值相关联,值可以是Python中的任何对象。

访问字典中的值

要获取与键相关联的值,可依次指定字典名和放在方括号内的键。

1
2
1 print(alien_0['color'])
green

添加键-值对

依次指定字典名、键、值。

1
2
3
4
1 alien_0['x_position'] = 0
2 alien_0['y_position'] = 25
3 print(alien_0)
{'color': 'green', 'points': 5, 'x_position': 0, 'y_position': 25}

创建空字典

1
2
3
4
5
1 alien_0 = {}        #定义空字典
2 alien_0['color'] = 'green'
3 alien_0['points'] = '5'
4 print(alien_0)
{'color': 'green', 'points': '5'}

修改字典中的值

1
1 alien_0['color'] = 'yellow'

删除键-值对

使用del语句将相应的键-值对彻底删除。使用del语句时,必须指定字典名和要删除的键。

1
2
3
4
5
6
7
1 alien_0 = {'color' : 'green','points': 5}
2 print(alien_0)
{'color': 'green', 'points': 5}

1 del alien_0['points'] #删除键值对
2 print(alien_0)
{'color': 'green'}

由类似对象组成的字典

请注意下面字典的定义格式,还有print输出格式。

1
2
3
4
5
6
7
8
9
10
11
1 favourite_languages = {					   #将一个较大的字典放在了多行中,用',隔开
2 'jen': 'python',
3 'sarah': 'c',
4 'edward': 'ruby',
5 'phil':'python',
6 }
7
8 print("Sarah's favourite language is" + #太长的print()可以用‘+’逻辑性的隔成多行。
9 favourite_languages['sarah'].title() +
10 '.')
Sarah's favourite language isC.

遍历字典

遍历所有的键-值对:items()

现有字典

1
2
3
4
5
1 user_0 = {
2 'username':'efermi',
3 'first':'enrico',
4 'last':'femi',
5 }

使用for循环遍历字典

1
2
3
1 or key,value in user_0.items():
2 print("\nkey:" + key)
3 print("Value:" + value)

item()方法返回一个键值对列表。注意,即使遍历字典时,键-值对的返回顺序也与存储顺序不同。Python不关系键-值对的存储顺序,而只跟踪键值之间的关联关系。

遍历所有的键:keys()

1
2
3
4
5
1 for name in user_0.keys():   #方法keys()返回一个列表,包含字典中的所有键。
2 print(name.title())
Username
First
Last

遍历字典时,会默认遍历所有的键,因此,将’user0.keys(‘)改为’user_0’,输出不变。

这种循环中,可以使用当前键来访问与之相关联的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 favourite_languages = {				#字典
2 'jen': 'python',
3 'sarah': 'c',
4 'edward': 'ruby',
5 'phil':'python',
6 }
7
8 friends = ['phil','sarah'] #列表
9 for name in favourite_languages.keys():
10 print(name.title())
11
12 if name in friends:
13 print("Hi" + name.title() +", I see your favourite_language is" +
14 favourite_languages[name].title() + "!") #访问字典的值

字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的。要以特定的顺序返回元素,一种方法是在for循环中对返回的键进行排序。

顺序遍历所有键:sorted()

1
2
3
4
5
6
1 for name in sorted(favourite_language.keys()):
2 print(name.title() + ',thank u for taking the poll')
Edward,thank u for taking the poll
Jen,thank u for taking the poll
Phil,thank u for taking the poll
Sarah,thank u for taking the poll

遍历所有值:values()

1
2
3
4
5
6
1 for language in favourite_languages.values():
2 print("the favourite languages is " + language.title())
the favourite languages is Python
the favourite languages is C
the favourite languages is Ruby
the favourite languages is Python

为剔除重复项,可使用集合set,集合类似于列表,但每个元素都必须是独一无二的。

1
2
3
4
5
1 for language in set(favourite_languages.values()):
2 print("the favourite languages is " + language.title())
the favourite languages is C
the favourite languages is Ruby
the favourite languages is Python

嵌套

有时候,需要将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套

字典列表

1
2
3
4
5
6
7
8
1 alien_0 = {'color':'green','points':'5'}     #alien字典
2 alien_1 = {'color':'yellow','points':'10'}
3 alien_2 = {'color':'red','points':'15'}
4
5 aliens = [alien_0,alien_1,alien_2] #aliens列表
6
7 for alien in aliens:
8 print(alien)

列表字典

1
2
3
4
5
6
7
8
9
10
11
  #存储此pizza的信息
1 pizza = {
2 'crust': 'thick',
3 'toppings': ['mushrooms','extra cheese'],
4 }
5 #概述所点的pizza
6 print("You ordered a " + pizza['crust'] + '-crust pizza ' +
7 "with the following toppings:")
8
9 for topping in pizza['toppings']:
10 print("\t" + topping)

字典字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1 users = {
2 'GK': {
3 'first':'Grace',
4 'last': 'Koo',
5 'location':'6Lanzhou',
6 }, #这是一个键值对,‘,’莫忘记
7 'BJ':{
8 'first':'Benedict',
9 'last':'Jin',
10 'location':'Yangzhou',
11 },
12 }
13 for username,user_info in users.items():
14 print("\nUsername:" + username)
15 full_name = user_info['first'] + " " + user_info["last"]
16 location = user_info['location']
17
18 print("\tFull name:" + full_name.title())
19 print("\tlocation: " + location.title())

Username:GK
Full name:Grace Koo
location: 6Lanzhou

Username:BJ
Full name:Benedict Jin
location: Yangzhou

使用标准库中的Collection优化字典

字典不记输入顺序,要记录键-值输入顺序,可使用模块collections中的OrderedDict类。

1
2
3
4
5
6
7
8
9
10
from collections import OrderedDict

favourite_languages = OrderedDict()

favourite_languages['jen'] = 'python'
favourite_languages['sarah'] = 'C'

for name,language in favourite_languages.items():
print(name.title() + "'s favourite language is" +
language.title() + ".")

用户输入和while循环

1
2
3
4
1 message = input("Tell me something,and I will repeat it back to u:")
2 print(message)
Tell me something,and I will repeat it back to u:hello,python
hello,python

input接受一个参数,即向用户显示的信息。程序等待用户输入,并在用户按回车键后继续执行。输入存储变量在message中。

出于清晰考虑,若提示信息太长,可以使用以下方法。

1
2
3
4
5
6
7
8
9
1 prompt = "If you tell us who u are,we can personalize the message you see."
2 prompt += "\nwhat is your first name?" #在字符串末尾添加一个字符串
3 name = input(prompt)
4 print("\nHello," + name +"!")

If you tell us who u are,we can personalize the message you see.
what is your first name?Grace

Hello,Grace!

用户输入input()

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

input()将用户输入的内容解释成字符串,但是如果用户只是想输入数值,就会遇到麻烦。对此,使用int()函数来将字符串转化为数值

1
2
3
4
5
6
7
1 height = input("How tall are u,in inches?")
2 height = int(height)
3
4 if height >= 36:
5 print("You are old enough to ride!")
6 else:
7 print("\nYou'll be able to ride when you're a little older.")

求模运算符“%”

返回余数。可用来求奇偶性。

1
2
1 4%3
1

While循环简介

while循环不断的运行,直到指定的条件不满足为止。

1
2
3
4
5
6
7
8
9
1 current_number = 1
2 while current_number <= 5:
3 print(current_number)
4 current_number += 1
1
2
3
4
5
1
2
3
4
5
6
7
8
9
10
11
12
#获取用户输入,输入“quit”退出程序
1 prompt = "\nTell me something,and I will repeat it back to u:"
2 prompt += "\nEnter 'quit' to end the program."
3 message = ""
4 while message != "quit":
5 message = input(prompt)
6 if message != 'quit' #选择不打印quit
7 print(message)

Tell me something,and I will repeat it back to u:
Enter 'quit' to end the program.ui
ui

使用标志

在要求很多条件都必须满足才能继续运行的程序中,可定义一个变量,用于判断整个程序是否处于活跃状态。

1
2
3
4
5
6
7
8
9
10
11
#标志
1 prompt = "\nTell me something,and I will repeat it back to u:"
2 prompt += "\nEnter 'quit' to end the program."
3
4 active = True
5 while active:
6 message = input(prompt)
7 if message == 'quit':
8 active = False
9 else:
10 print(message)

简化了while语句,因为不需要在其中作任何比较

使用break跳出循环

使用break立即跳出while循环。

1
2
3
4
5
6
7
8
9
10
1 prompt = "\nTell me something,and I will repeat it back to u:"
2 prompt += "\nEnter 'quit' to end the program."
3
4 while True:
5 city = input(prompt)
6
7 if city == "quit":
8 break
9 else:
10 print("I'd love to go to" + city.title() + "!")

在循环中使用continue

返回到循环开头。

1
2
3
4
5
6
7
8
9
10
11
12
#打印奇数
1 current_number = 0
2 while current_number <10:
3 current_number += 1
4 if current_number %2 == 0:
5 continue
6 print(current_number)
1
3
5
7
9

用while循环来处理列表和字典

通过将while循环同列表和字典结合起来使用,可收集、存储并组织大量输入,供以后查看和显示。

在列表之间移动元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1 unconfirmed_users = ['alice','brian','cadace']     #未验证用户
2 confirmed_users = [] #已验证用户
3
4 while unconfirmed_users:
5 current_user = unconfirmed_users.pop() #删除未验证用户
6 print("Verifying user:" + current_user.title())
7 confirmed_users.append(current_user) #添加到已验证用户
8
9 print("\nThe following users have been confirmed:")
10for confirmed_user in confirmed_users:
11 print(confirmed_user.title())

Verifying user:Cadace
Verifying user:Brian
Verifying user:Alice

The following users have been confirmed:
Cadace
Brian
Alice

删除包含特定值的所有列表元素

函数remove()只删除一个特定元素,想要删除所有,使用while。

1
2
3
4
5
6
7
8
9
10
1 pets = ['dog','cat','dog','goldfish','cat','rabbit','cat']
2 print(pets)
3
4 while 'cat' in pets: #使用while找到元素,再remove()一个。
5 pets.remove('cat')
6
7 print(pets)

['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
['dog', 'dog', 'goldfish', 'rabbit']

使用用户输入来填充字典

可使用while循环提示用户输入任意数量的信息。将收集的信息存储在一个字典中,将回答同被调查者联系起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1 responses = {}       				#问卷字典
2 polling_active = True #标记
3
4 while polling_active:
5 name = input("\nWhat's your name?")
6 response = input("Which mountain would you like to climb someday?")
7
8 responses[name] = response #将信息存储在字典中
9
10 repeat = input("Would u like to let another person respond?(yes/no)")
11 if repeat == 'no':
12 polling_active = False
13
14 print("\n------Polling Results-------")
15 for name,response in responses.items():
16 print(name + "would like to climb " + response)

What's your name?grace
Which mountain would you like to climb someday?huangshan
Would u like to let another person respond?(yes/no)y

What's your name?benedict
Which mountain would you like to climb someday?huangshan
Would u like to let another person respond?(yes/no)no

------Polling Results-------
gracewould like to climb huangshan
benedictwould like to climb huangshan

函数

每个函数都应只负责一项具体的工作,这有助于将复杂的任务划分成一系列的步骤。

定义函数

1
2
3
4
1 def greet_user(username):     #函数定义
2 print("Hello," + username + "!")
3 greet_user("Grace")
Hello,Grace!

传递实参

位置实参

位置实参的顺序很重要,每个实参都必须关联到函数定义的一个形参。

关键字实参

关键字实参是传递给函数的名称-值对。直接在实参中将名称和值关联起来了。

1
2
3
4
5
1 def describe_pet(animal_type,pet_name):
2 print("My pet is " + animal_type + " It's name is " + pet_name)
3
4 describe_pet(pet_name = 'harry',animal_type = 'hamster')
My pet is hamster It's name is harry

默认值

1
2
3
4
5
1 def describe_pet(animal_type,pet_name = 'harry'):
2 print("My pet is " + animal_type + " It's name is " + pet_name)
3
4 describe_pet(animal_type = 'hamster')
My pet is hamster It's name is harry

需要注意的是,如果实参中只提供了一个参数,Python依然将这个实参视为位置实参,即将把这个实参关联到函数中的第一个形参中。所以,下面的写法会导致错误。

1
2
3
4
5
6
7
8
9
1 def describe_pet(animal_type = 'khah',pet_name):
2 print("My pet is " + animal_type + " It's name is " + pet_name)
3
4 describe_pet(pet_name = 'hamster')

File "<ipython-input-13-1f3b75aa0373>", line 1
def describe_pet(animal_type = 'khah',pet_name):
^
SyntaxError: non-default argument follows default argument

使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参。让python能够正确的解读出位置实参。

返回值

1
2
3
4
5
6
7
8
1 def get_formatted_name(first_name,last_name):
2 full_name = first_name + " " + last_name
3 return full_name.title()
4
5 musician = get_formatted_name("jimi","hendrix")
6 print(musician)

Jimi Hendrix
1
2
3
4
5
6
7
8
9
10
11
12
13
#检查名字,并输出
1 def get_full_name(first_name,last_name,middle_name = ""):
2 full_name = first_name + " " + middle_name +" "+ last_name
3 return full_name.title()
4
5 musician = get_full_name("jin","hen")
6 print(musician)
7
8 musician = get_full_name("jin","hen","lee")
9 print(musician)

Jin Hen
Jin Lee Hen

返回字典

1
2
3
4
5
6
7
8
9
10
1 def build_person(first_name,last_name,age = ''):
2 person = {"first":first_name,"last":last_name}
3 if age:
4 person['age'] = age
5 return person
6
7 musician = build_person("jimi","hendix",age = 27)
8 print(musician)

{'first': 'jimi', 'last': 'hendix','age': 27}

传递列表

假设有一个用户列表,我们要问候其中的每位用户。下面示例将一个名字列表传递给一个名为greet_users()的函数

1
2
3
4
5
6
7
8
9
10
1 def greet_users(names):
2 for name in names:
3 msg = "Hello," + name.title() + "!"
4 print(msg)
5 usernames = ['hannah','ty','margot']
6 greet_users(usernames)

Hello,Hannah!
Hello,Ty!
Hello,Margot!

禁止函数修改列表

可向函数传递列表的副本而不是原件,这样函数所做的任何修改都只影响副本,而丝毫不影响原件。

切片表示法[:]创建列表的副本。

1
1 function_name(list_name[:])

以上的list调用方法将调用list_name的副本,保留原始的内容,除非有充分的理由需要传递副本,否则还是应该将原始列表传递给函数,因为副本将花费时间和内存,降低效率,在处理大型列表时尤其如此。

传递任意数量的实参

有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参。

1
2
3
4
5
6
7
8
1 def make_pizza(*toppings):
2 print(toppings)
3
4 make_pizza('pepperoni')
5 make_pizza('mushrooms','green peppers','extra cheese')

('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')

形参名toppings中的”“让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。*注意:Python将实参封装到一个元组中,即便函数只收到一个值也如此。

结合使用位置实参和任意数量的实参

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

如前面的函数还需要一个size的实参,必须将该形参放在形参*toppings的前面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 def make_pizza(size,*toppings):
2 print("Making a" + str(size) +
3 "-inch pizza with the following toppings:")
4 for topping in toppings:
5 print("-" + topping)
6
7 make_pizza('pepperoni')
8 make_pizza('mushrooms','green peppers','extra cheese')

Making a16-inch pizza with the following toppings:
-pepperoni
Making a12-inch pizza with the following toppings:
-mushrooms
-green peppers
-extra cheese

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

预先不知道传递给函数的会是什么样的信息。可将函数编写成能够接受任意数量的“键-值”对————调用语句提供多少就接受多少。(匹配成字典

1
2
3
4
5
6
7
8
9
10
11
12
13
def build_profile(first,last,**user_info):
profile = {}
profile['first_name'] = first
profile['last_name'] = last
for key,value in user_info.items():
profile[key] = value
return profile

#传递两个键值对,location='princeton',filed='physics'
user_profile = build_profile('albert','einstein',location='princeton',filed='physics')
print(user_profile)

{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'filed': 'physics'}

将函数存储在模块中

将函数存储在被称为模块独立文件中,再将模块导入到主程序中,import语句允许在当前运行的程序文件中使用模块中的代码。

导入整个模块

要让函数是可导入的,得先创建模块。模块是扩展名为.py的文件,包含要导入到程序中的代码。

首先创建pizza.py文件,然后在pizza.py所在的目录中创建另一个名为making_pizza.py的文件,这个文件导入刚创建的模块,调用里面的函数两次。

1
2
3
4
import pizza

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

Python读取这个文件时,代码行import pizza让Python打开文件pizza.py,并将其中的所有函数都复制到这个程序中。

导入特定的函数

导入模块中特定的函数。

1
from module_name import function_name1,function_name2
1
2
3
4
from pizza import make_pizza

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

由于在import中显式的使用了函数名,因此调用它无需只需指定其名称。

使用as给函数指定别名

如果要导入的函数名称可能与程序中现有的名称冲突,或者函数名称太长,可指定别名。关键字as将函数重命名为你提供的别名。

1
2
3
4
from pizza import make_pizza as mp

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

使用as给模块指定别名

1
2
3
4
import pizza as p

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

导入模块中的所有函数

1
2
3
from pizza import *

make_pizza(16,'pepperoni')

但是这种方法不推荐。最佳做法,要么只导入你需要的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰。

函数编写指南

  1. 函数起名描述性名称,且只使用小写字母和下划线。
  2. 注释应紧跟在函数定义后面。
  3. 给形参指定默认值时,等号两边不要有空格。
  4. 函数调用中的关键字实参,也不要有空格。

面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。基于类创建对象时,每个对象都自动具备这种通用的行为,然后可根据需要赋予每个对象独特的个性。根据类来创建对象被称为实例化。

创建和使用类

创建Dog类

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog():
"""一次模拟小狗的简单尝试"""

def __init__(self,name,age):
"""特殊的方法,初始化,两个下划线"""
self.name = name
self.age = age

def sit(self):
print(self.name.title() + "is now sitting.")

def roll_over(self):
print(self.name.title() + "rolled over!")
  1. 定义了一个名为Dog的类。在Python中,首字母大写的名称指的是类。
  2. 方法 init(),类中的函数称为方法。init方法是一个特殊的方法,每当根据Dog类创建对象新实例时,Python都会自动执行它。这个方法中,开头和末尾有两个下划线,这是约定。
  3. 注意!!!方法init 是以两个下划线开始和结束的。
  4. 形参self必不可少,还必须位于其他形参的前面。Python调用这个init方法来创建Dog实例时,将自动传入实参self。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。我们创建Dog类实例时,Python将调用Dog类的方法init()。我们通过实参传递名字和方法,self会自动传递,因此我们不需要给self提供值。
  5. 通过类的构造函数中的参数传入的这些数据也想在各个方法中被使用,就需要在类中长久保存并能随时调用这些数据,为解决这个问题,在类中,所有传入的数据都赋给了一个变量,就是self。它接收实例化过程中传入的所有数据。
  6. 以self为前缀的变量都可供类中的所有方法使用。self.name = name获取存储在形参name中的值,并将其存储到变量name中,然后该变量被关联到当前创建的实例。想这样可以通过实例访问的变量称为属性
  7. sit和roll_over方法。由于这些方法不需要额外的信息,如名字或年龄,因此它们只有一个形参self。

根据类创建实例,访问类

1
2
3
4
5
6
7
8
my_dog = Dog("willie",6)
"""在类中这两个参数被传给了self参数"""

print("My Dog's name is " + my_dog.name.title() + ".")
"""访问类属性"""

my_dog.sit()
"""访问类方法"""

在类中访问name属性时,使用self.name;在类外使用my_dog.name。

修改属性的值

1.直接在类外修改属性的值

2.通过方法修改属性的值

1
2
3
4
5
6
7
8
9
class Dog():
--snip--

def update_name(self,new_name):
"""更新姓名"""
self.name = new_name

my_dog = Dog("willie",6)
my_dog.update_name("hahaha")

继承

一个类继承另一个类时,它将自动获得另一个类的所有属性和方法。同时还可以定义自己的属性和方法。

子类的方法__init__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Car():
"""汽车"""

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

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

class ElectricCar(Car):
"""电动汽车,集成父类Car"""

def __init__(self,make,model,year):
"""初始化父类的属性"""
super().__init__(make,model,year)

"""子类特有的属性"""
self.battery_size = 70

def describe_battery(self):
print("this car has a" + str(self.battery_size) + "-kWh battery")

my_tesla = ElectricCar('tesla','model s',2016)
"""调用父类的方法"""
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

2016 Tesla Model S
this car has a 70-kWh battery

创建子类时,父类必须包含在当前文件中,且位于子类的前面。(在子类模块中,必须导入父类来实现继承。)定义子类时,必须在括号内指定父类的名称。super()调用父类方法init()。可以在子类中创建新的属性和方法。

重写父类的方法

在子类中定义一个与父类方法同名的方法。

1
2
3
4
5
class ElectricCar(Car):
--snip--

def fill-gas_tank():
"""重写父类方法,直接覆盖"""

类的实例作为另个类的属性

如上例中,将电瓶划分出来作为一个新类,作为电瓶车类的一个属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Car():
--snip--
class Battery():
"""电瓶"""
def __init__(self,battery_size=70):
self.battery_size = battery_size

def battery.describe_battery():
print("haha")

class Electricity(Car):
--snip--
"""类Battery作为属性出现在init()中"""
self.battery = Battery()

my_tesla = ElectricCar('tesla','model's',2016)

"""引用 属性(类)的方法"""
my_tesla.battery.describe_battery()

导入类

将类存储在模块中,然后在主程序中导入所需的模块。

导入单个类

创建Car类,并保存为car.py。创建另一个文件my_car.py,其中导入Car类并创建其实例。

1
2
3
4
5
"""my_car.py"""
from car import Car

my_new_car = Car('audi','a4',2016)
my_new_car.get_descriptive_name()

在一个模块中存储多个类

也可以将多个类存储在同一个模块中,导入方法和上节相同。

从一个模块中导入多个类

1
from car import Car,ElectricCar

导入整个模块

你可以导入整个模块,再使用句点表示法访问需要的类。

1
2
3
4
import car

"""句点表示法"""
my_tesla = car.ElectricCar('tesla','model's',2016)

8.3.5 导入模块中所有的类

1
from car import *

但是不推荐这种方法,需要从一个模块中导入很多类时,最好导入整个模块,并使用module.class_name语法来访问类。这样做你可以清楚的知道程序中使用了哪些导入的类。还避免了导入模块中的每个类可能引发的名称冲突。

编码规则

  1. 类名采用驼峰命名法,类中每个单词首字母大写,而不适用下划线。

​ 实例名和模块名采用小写格式,在单词间加上下划线。

  1. 在类后加上文档字符串作为说明。模块也是,在开头。
  1. import先导入标准库中的。再导入你自己写的。两者用一个空行隔开。

文件和异常

从文件中读取数据

读取整个文件

打开并读取文件,并将内容显示到屏幕上。

1
2
3
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)

1.函数open()返回一个表示文件的对象。在这里open(‘pi_digits.txt’)返回一个表示文件pi_digits.txt的对象,Python将这个对象存储在我们将在后面使用的变量中。

2.关键字with在不再需要访问文件后将其关闭。注意我们使用了open(),但没有使用close(),你也可以用open和close来打开和关闭文件,但这样做时,如果程序存在bug,导致close未执行,文件将不会关闭,未妥善的关闭文件可能导致数据丢失或受损。并非在任何情况下都能轻松的确定关闭文件的恰当时机,但通过使用前面使用的结构,可让python 去确定:你只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭

3.有了表示pi_digits.txt的文件对象后,我们使用方法read(),读取文件文件的全部内容。

4.read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除末尾的空行,可在print中使用rstrip()

1
print(contents.rstrip())

逐行读取

要以每次一行的方式检查文件,可对文件对象使用for循环。

1
2
3
4
filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line.rstrip())

文件路径

相对文件路径:相对当前的工作目录。

在LInux和OS X中:

1
with open('text_files/filename.txt') as file_object:

在Windows中:使用反斜杠

1
with open('text_files\filename.txt') as file_object:

绝对文件路径:无需关心,给出完整目录。

在LInux和OS X中:

1
2
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:

在Windows中:使用反斜杠

1
2
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:

建包含文件各行内容的列表

使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表。

1
2
3
4
5
6
filename = 'pi_digits.txt'
with open(filename) as file_object:
lines = file_object.readlines()

for line in lines:
print(line.rstrip())

readlines()从文件中读取每一行,并将其存储在一个列表中。

如果要将文件作为一个字符串打印出来,可以使用下面的方法。

1
2
3
4
5
6
7
8
9
10
11
"""以字符串形式打印文件的前499个字符"""
filename = 'pi_digits.txt'

with open(filename) as file_object:
lines = file_object.readlines()

pi_string = ''
for line in lines:
pi_string += line.strip()

print(pi_string[:500] + "......")

字符串是否包含在文件中

先将文件整理成字符串,在上节代码最后添加:

1
2
3
4
5
6
7
--snip--
input_str = input("Input your string that you want to check:")
if input_str in pi_string:
"""检查是否包含字符串"""
print("Yes")
else:
print("No")

写入文件

写入空文件

1
2
3
4
filename = 'writef.txt'

with open(filename,'w') as file_object:
file_object.write("I love programming.")

注:1.open()提供了两个参数,第一个是要打开的文件名称,第二个是打开模式

​ 2.可以以只读模式”r“、写入模式“w”、附加模式“a”打开文件。或让你能够读取和写入文件的模式“r+”,如果你省略了模式参数,Python将默认为只读模式

​ 3.如果你要打开的文件不存在,函数open()将自动创建它。然而,以写入模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空文件

​ 4.Python只能将字符串写入文本文件。要将数值存储,需使用str()转化为String

写入多行

函数write()不会在你写入的文本末尾添加换行符。要写多行,使用换行符。

1
2
file_object.write("I love programming.\n")
file_object.write("I love programming very much.\n")

附加到文件

给文件添加内容,而不是覆盖。

1
2
with open(filename,'a') as file_object:
file_object.write("I love programming.\n")

异常

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

异常使用try-except代码块处理。

ZeroDivisionError异常

1
2
3
4
5
6
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")

You can't divide by zero!

try代码块中的代码运行起来没有问题,Python将跳过except代码块;如果有问题,Python将会查找Except代码块

依赖于try代码块成功执行的代码都应该放到else代码块中:

1
2
3
4
5
6
7
8
try:
answer = 5/3
except ZeroDivisionError:
print("you can't divide 0!")
else:
print(answer)

1.6666666666666667

FileNotFoundError异常

1
2
3
4
5
6
7
8
9
filename = 'alice.txt'

try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
print("your file is not exisit")

your file is not exisit

分析文本split()

split(),根据一个字符串创建一个单词列表。

1
2
3
4
title = "Alice in Wonderland"
title.split()

['Alice', 'in', 'Wonderland']

split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。

失败时一声不吭

如果你想让try代码块在发生异常后医生不吭,Python有一个pass语句,可在代码块中使用它来让Python“沉默”

1
2
3
4
5
6
try:
answer = 5/0
except ZeroDivisionError:
pass
else:
print(answer)

存储数据

程序需要把用户提供的信息存储在列表和字典等数据结构中。用户关闭程序时,你几乎总是要保存他们提供的信息。一种简单的方式是使用模块json来存储数据。【 JSON(JavaScript Object Notation) 】

使用json.dump()和json.load()

函数json.dump()接受两个实参:1.要存储的数据 2.可用于存储数据的文件对象。下面展示用其存储数字列表。

1
2
3
4
5
6
7
8
"""使用json.dump()写入数据"""
import json

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

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

json.load()参数与上面一样。

1
2
3
4
5
6
7
8
9
10
"""使用json.load()读取数据"""
import json

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

print(numbers)

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

为什么使用json

对于用户生成的数据,使用json保存它们大有裨益,因为如果不以某种方式进行存储,等程序停止运行时用户的信息将丢失。下面看个例子,用户首次运行被提示输入自己的名字,这样再次运行程序就记住他了

1
2
3
4
5
6
7
8
9
10
11
12
"""存储用户的名字"""
import json

username = input("what's your name?")

filename = 'username.json'
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("we'll remember you when you come back," + username + "!")

what's your name?GraceKoo
we'll remember you when you come back,GraceKoo!

再来编写一个程序,向其名字被存储的用户发出问候。

1
2
3
4
5
6
7
8
9
import json

filename = 'username.json'

with open(filename) as f_obj:
username = json.load(f_obj)
print("welcome back," + username +"!")

welcome back,GraceKoo!

将以上两个程序进行合并:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json

#如果以前存储了用户名,就加载它,否则,就提示用户输入用户名并存储它
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("what's your name?")
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("we'll remember you when you come back," + username + "!")
else:
print("Welcome back," + username + "!")

重构

代码能正确运行,但可做进一步的改进————-将代码划分为一系列完成具体工作的函数。这样的过程叫做重构。重构让代码更清晰、更易于理解、更容易扩展。

假若重构上列代码,可将其大部分逻辑放到一个或多个函数中。比如:get_stored_username()用于获取存储了的用户名,get_new_username()用于存储新的用户名,greet_user()相当于主函数,在三个函数外部首先被调用,它进行判断,调用相应的函数,对不同的用户显示不同的信息。这样三个函数分工明确,代码结构也清晰明了。

测试代码

在本章中,将学习如何用Python模块unittest中的工具来测试代码。将学习编写测试用例。

测试函数

测试代码为:(name_function.py)

1
2
3
4
def get_formatted_name(first,last):
"""返回整洁的名字"""
full_name = first + '' + last
return full_name.title()

为测试上例代码,编写一个使用这个函数的程序,下例让用户输入名和姓。

1
2
3
4
5
6
7
8
9
10
11
12
13
from name_function import get_formatted_name

print("Enter 'q' at any time to quit")
while True:
first = input("\nPlease give me a first name:")
if first == 'q':
break
last = input("\nPlease give me a last name:")
if last == 'q':
break

format_name = get_formatted_name(first,last)
print("\tNeatly formatted name:" + format_name + '.')

单元测试和测试用例

Python标准库中的模块unittest提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。

创建测试用例的语法需要一段时间才能习惯,但测试用例创建后,再添加针对函数的单元测试就很简单了。

编写测试用例, 1.可先导入模块unittest以及要测试的函数。

​ 2.再创建一个继承unittest.TestCase的类。并编写一系列方法对函数行为的不用方面进行测试。

下面是一个只包含一个方法的测试用例,它检查函数get_formatted_name()在给定名和姓时能否正常工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import unittest
from name_function import get_formatted_name
"""1.导入模块unittest和要测试的函数get_formatted_name()"""

class NameTestCase(unittest.TestCase):
"""2.创建测试类,继承自TestCase"""

def test_first_last_name(self):
"""3.测试name_function.py"""
"""能够正确地处理像Janis Joplin这样的姓名吗?"""
formatted_name = get_formatted_name('janis','joplin')
"""4.一个断言方法"""
self.assertEqual(formatted_name,'Janis Joplin')

"""5.运行这个文件中的测试"""
unittest.main()

我们运行测试用例时,所有以test_打头的方法都将自动运行。

这个断言方法用来核实得到的结果是否与期望的结果一致。asserEqual()意思是说,进行比较,如果它们相等,就万事大吉,如果不等,跟我说一声!

测试类

各种断言方法

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中

一个要测试的类

Survey.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)

再来编写一个使用它的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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()

测试AnonymousSurvey

下面来编写一个测试,对AnonymousSurvey类的行为的一个方面进行验证:如果用户对面对调查问题时只提供了一个答案,这个答案也能被妥善的存储。为此,我们将在这个答案被存储后,使用方法assertIn()来核实它包含在答案列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
"""针对AnonymousSurvey类的测试"""

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.response)

unittest.main()

方法setUp()

unittest.TestCase类包含方法SetUp(),让我们只需创建这些对象一次,并在每个测试方法中使用它们。如果你在TestCase类中包含了方法setUp(),Python将先运行它,在运行各个以test_打头的方法。这样,你在编写的每个测试方法中都可以使用在方法setUp()中创建的对象了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import unittest

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.response[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)

unittest.main()

包含self,即存储在类的属性中。测试自己写的类时,在setUp()中创建一系列实例并设置它们的属性,在测试方法中直接使用这些实例。

每完成一个单元测试,Python打印一个字符:测试通过打印一个句点;测试引发错误打印一个E;测试导致断言失败打印一个F。

参考书籍

  • 《Python编程:从入门到实践》