第一部分 基础知识
回到主頁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # python更新指令 pip install --upgrade python # pip更新指令 python.exe -m pip install --upgrade pip # 国内镜像安装 pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple some-package pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple some-package pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple some-package # pip查看可更新包 pip list --outdated # 批量升级所有包,升级前先确认 pip-review --local --interactive # 一键升级所有包 pip-review --auto
前面的安装环节网络上讲得比书上的要详细多了,官方文档也讲得很清楚,所以这里就不在这里说了,有兴趣可以去访问官方网站😺 。
第一章 变量和简单的数据类型
变量
变量命名有一定的规则
不能是数字开头,比如:1_message
不能包含空格,比如:mess age
不能是python里面的关键字,比如:print 或 def
等,python里面有很多关键字一般你打出来会高亮显示
最好的变量名应该是既简短又有描述性,比如:name、student_name
等等。
这里message是变量名,'Hello Python
World!'是指向message变量的值。变量是可以重复赋值的,但它只会储存最后的值。
1 2 3 4 5 6 7 message = 'Hello Python World!' print (message) Hello Python World! message = '你好!' print (message) 你好!
字符串
有双引号或单引号包裹的内容就称为字符串,有些编程语言只有双引号是字符串。
1 2 "This is string." 'This is string.'
1.方法:title()、upper()、lower()
方法(method)是python对数据执行的操作,name后面的点(.)是让python对name变量执行
title()
方法指定的操作,这个方法就是把单词的手写字幕改成大写。upper() 方法是让所有字母变成大写、lower() 方法是让所有字母变成小写。还有一个方法修改字符串中的指定单词。replace() 这个方法我们在后面的练习中会用到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 name = "zheng kai nan" print (name.title()) Zheng Kai Nan name = "Zheng Kai Nan" print (name.upper()) ZHENG KAI NANprint (name.lower()) zheng kai nan name = "zheng kai nan" name_01 = name.replace("kai nan" , "ji zhong" )print (name)print (name_01) zheng kai nan zheng ji zhong
2.在字符串中插入变量
这个功能很常用,python的方式是在字符串的引号前面加一个
f 字母:
1 2 3 4 5 6 7 first_name = "zheng" last_name = "kai nan" full_name = f"{first_name} {last_name} " print (full_name) zheng kai nanprint (f"Hello,{full_name.title()} !" ) Hello,Zheng Kai Nan!
**3.制表符和换行符控制字符串 *
在字符串中添加、 *可以缩进和换行,字符串引号前加r
可以让取消里面所有转义符号:
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 message = "你知道这几种编程语言吗?Python Swift C++ Go Java" print (message) 你知道这几种编程语言吗?Python Swift C++ Go Java message = "\t你知道这几种编程语言吗?Python Swift C++ Go Java" print (message) 你知道这几种编程语言吗?Python Swift C++ Go Java message = "你知道这几种编程语言吗?\nPython Swift C++ Go Java" print (message) 你知道这几种编程语言吗? Python Swift C++ Go Java message = "你知道这几种编程语言吗?\n\tPython \n\tSwift \n\tC++ \n\tGo \n\tJava" print (message) 你知道这几种编程语言吗? Python Swift C++ Go Javaprint (r"\nasd" ) \nasd
4.删除字符串里面的空白rstrip()、lstrip()、strip()
有时候后输入字符串的时候会多输空格,在python里面多一个空格就意味着两个不同的字符串了。这里有3个方法:分别是rstrip()、lstrip()、strip() ,rstrip() 是删除字符串右边的空格,lstrip() 是删除左边的空格,strip() 是删除两边的空格。但是这种删除只是暂时的,要想永久删除就要重新赋值给变量。
1 2 3 4 5 6 7 8 9 10 11 language = ' python ' print (language.rstrip())' python' print (language.lstrip())'python ' print (language.strip())'python' language = language.strip()print (language)'python'
5.删除前缀和后缀 removeprefix()、removesuffix()
有些内容有统一的前缀,比如你有很多照片,前缀是某一个时间比如2023.7.25+名字或序号,你就只想要序号或名字就可以用到这个功能,还有就是URL里面的https://这个是网站前缀,就可以用removeprefix() 括号里面填写你想删除的前缀,用引号括起来。这种方法的删除也是暂时的,要想永久删除需要重新赋值,和上面的删除空白一样。
1 2 3 4 5 6 7 8 9 10 11 12 Travel_photo = "2023.7.25-镇远旅游照片" print (Travel_photo.removeprefix('2023.7.25-' )) 镇远旅游照片 url = "https://www.baidu.com" print (url.removeprefix('https://' )) www.baidu.com file_name = 'abc.jpg' print (file_name.removesuffix('.jpg' )) abc
数
可以理解为数学里面的数字,用来计算或可视化数据
1.整数及其运算
integer()或简写int()表示整数,**+、-、*、/
这几个符号表示加减乘除,还有些复杂的后面会讲到,比如( )两个星号代表乘方运算。
2.浮点数
float()
表示浮点数,就是带有小数点的数称为浮点数,数字同样都可以应用上面的计算符号。
3.数字中的下划线
下划线用在数字中,并不会有其他的效果,只是方便我们更好观察。
1 2 3 number = 1000_000_000 print (number)10000000000
4.同时给多个变量赋值
同时给多个变量赋值,需要用逗号将变量名分开,对于要赋给变量的值也需要做同样的事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 x,y,z = 1 ,2 ,3 x = 1 y = 2 z = 3 x,y,z = 1 ,2 print (x) 发生异常: ValueError xnot enough values to unpack (expected 3 , got 2 ) File "F:\第一章变量和简单数据类型\full_name.py" , line 7 , in <module> x, y, z = 1 , 2 ^^^^^^^ ValueError: not enough values to unpack (expected 3 , got 2 )
5.常量
Python里面没有常量(就是整个程序的生命周期不改变值的“变量”),一般是用全大写字母来共同与其他程序员形成约定,遇到全大写字母的变量时,视为常量。
6.注释
注释的主要目的是阐述代码要做什么,以及是如何做的。
小结
本章我们学习了如何使用变量,创建了描述性的变量名,学习了字符串是什么,以及如何使用全大写、全小写和首字母大写的方式显示字符串和制表符、换行符。还学习了如何删除字符串中多余的字符和空格,以及字符串中插入变量的方法。还学习了整数和浮点数,还有学习了写注释的目的。
第二章 列表简介
列表是什么
列表(list)由一系列按特定顺序 排列的元素组成,一般给列表命名以复数形式,比如:name就用names。
在python中列表用“[
]”表示,每个元素用逗号隔开。列表里面有两个东西要搞清楚,一个是索引,一个是元素的值。要访问列表时使用索引和元素值都可以,列表的索引是从0开始的,所以要访问的n个元素就使用n-1的索引值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bicycles = ['trek' , 'cannondale' , 'redline' , 'specialized' ]print (bicycles) ['trek' , 'cannondale' , 'redline' , 'specialized' ]print (bicycles[0 ])print (bicycles[3 ]) trek specializedprint (bicycles[-1 ]) specialized message = f'我喜欢{bicycles[0 ]} 品牌的摩托车。' print (message) 我喜欢trek品牌的摩托车。
修改、添加和删除列表元素
大多数列表将是动态的,意味着列表创建后,随着程序的运行将修改、增加、或删除其中的元素。
这中间增加有append() 方法在列表末尾添加元素,insert() 在列表的指定位置插入元素。
删除有del
列表元素 将删除列表元素并且无法在访问这个元素了。
pop() 默认删除列表最后一个元素,也可以指定删除列表中的其他元素,在括号里面填上该元素的索引值就可以了。
这里说下,pop()删除的元素可以赋值给一个变量继续使用,不像del不能在赋值和访问了。
remove() 方法是删除一个指定的元素值,列表有多个同样的值的话这个方法只删除第一个,要想全部删除就要使用循环。
值得一提的是这个方法和pop()一样删除了的元素可以赋值给一个变量继续使用。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ] motorcycles[0 ] = 'da yang' print (motorcycles) ['da yang' , 'yamaha' , 'suzuki' ] motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ] motorcycles.append('da yang' )print (motorcycles) ['honda' , 'yamaha' , 'suzuki' , 'da yang' ] motorcycles.insert(0 , 'li fan' )print (motorcycles) ['li fan' , 'honda' , 'yamaha' , 'suzuki' , 'da yang' ] motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ]del motorcycles[0 ]print (motorcycles) ['yamaha' , 'suzuki' ] motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ] poped_motorcycles = motorcycles.pop()print (motorcycles) ['honda' , 'yamaha' ]print (poped_motorcycles) suzuki motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ] last_motorcycles = motorcycles.pop(0 )print (motorcycles) ['yamaha' , 'suzuki' ]print (last_motorcycles) honda motorcycles = ['honda' , 'yamaha' , 'suzuki' ]print (motorcycles) ['honda' , 'yamaha' , 'suzuki' ] mid_motorcycles = 'yamaha' motorcycles.remove(mid_motorcycles)print (motorcycles) ['honda' , 'suzuki' ]print (f'{mid_motorcycles.title()} ' ) Yamaha
列表管理
1.sort() 方法可以对列表永久排序 ,该方法是将列表按字母顺序或数字从小到大的顺序进行排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cars = ['d' , 'f' , 'c' , 'b' , 'a' , 'e' ]print (cars) ['d' , 'f' , 'c' , 'b' , 'a' , 'e' ] cars.sort()print (cars) ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ] cars.sort(reverse=True )print (cars) ['f' , 'e' , 'd' , 'c' , 'b' , 'a' ] cars = cars[::-1 ]print (cars) ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]
2.
sorted() 方法是对列表进行临时排序 。
1 2 3 4 5 print (sorted (cars, reverse=True )) ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]print (cars) ['f' , 'e' , 'd' , 'c' , 'b' , 'a' ]
3.reverse() 方法是永久反转排列 列表里面的元素。
1 2 3 cars.reverse()print (cars) ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]
4.len() 方法可以快速获取列表长度,其显示的是列表内有多少个元素。
5.enumerate()
函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标 ,一般用在
for 循环当中。
1 2 3 4 5 6 7 >>> seq = ['one' , 'two' , 'three' ]>>> for i, element in enumerate (seq):... print i, element ...0 one1 two2 three
小节
本章学习了什么是列表,以及如何使用列表内的元素。学习了定义列表,增删改列表内元素,以及如何对列表进行永久排序和临时排序,还学习了反转列表顺序和反向排序。
第三章 操作列表
遍历整个列表
使用for 循环可以很轻松的访问整个列表,以及对列表进行操作。这里值得一提的是for循环里面的变量名,在遍历列表是尽可能用列表的单数名,比如列表名为cars,那么for循环的变量尽量写成car。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 magicians = ['alice' , 'david' , 'carolina' ]for magician in magicians: print (magician) alice david carolinafor magician in magicians: print (f'{magician.title()} ,你的表演很精彩!' ) print (f'谢谢你,{magician.title()} ' )print ('感谢你们每一个人的表演' ) Alice,你的表演很精彩! 谢谢你,Alice David,你的表演很精彩! 谢谢你,David Carolina,你的表演很精彩! 谢谢你,Carolina 感谢你们每一个人的表演
创建数值列表
range() 函数可以生成一系列数,但它实际上不会打印最后一个数,这是编程语言中常见的差一 行为结果,要想打印最后个数需要使用+1或者是使用比最后一个数大1的数
1 2 3 4 5 6 7 8 for i in range (1 , 5 ): print (i, end=' ' )1 2 3 4 for i in range (1 , 5 +1 ): print (i, end=' ' )1 2 3 4 5
list() 函数可以将range()的结果直接转换为列表,方法是将range()作为list()的参数,同时range()还可以指定步长。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 number = list (range (1 , 6 ))print (number) [1 , 2 , 3 , 4 , 5 ] even_number = list (range (2 , 11 , 2 ))print (even_number) [2 , 4 , 6 , 8 , 10 ] squares = []for value in range (1 , 11 ): square = value**2 squares.append(square)print (squares) [1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 , 100 ]
使用这几个函数可以对数值列表进行统计列表作为其参数 ,min()列表里面最小值,max()列表里面最大值,sum()列表所有数求和。
1 2 3 4 5 6 7 8 values = [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]print (min (values))0 print (max (values))9 print (sum (values))45
列表推导式 是一种简化方法,这种方法首先指定一个列表名(变量名),然后等号右边用中括号开始,括号内的排列顺序是表达式------for循环,这种方法需要经常练习。比如创建上面数值列表可以写成:
1 2 3 4 5 6 7 8 9 squares = [value**2 for value in range (1 , 11 )]print (squares) [1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 , 100 ] jishu_num = list (range (1 , 50 , 2 ))print (sum (jishu_num))625 print (sum ([jishu for jishu in range (1 , 50 , 2 )]))625
使用列表的一部分
切片 ,要使用列表的一部分,其实就是使用列表的索引。使用的方法是在调用列表时在列表名后面[0:1],这代表列表的第一个和第二个值,就是在列表索引值0,1的值。这里面也要注意差一行为。在一个班级需要评出前三名或后三名或部分名单时,切片可以起到很好的作用。
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 players = ['charles' , 'martina' , 'michael' , 'florence' , 'eli' ]print (players[1 :3 ]) ['martina' , 'michael' ]print (players[:3 ]) ['charles' , 'martina' , 'michael' ]print (players[1 :]) ['martina' , 'michael' , 'florence' , 'eli' ]print (players[:]) ['charles' , 'martina' , 'michael' , 'florence' , 'eli' ]print (players[-3 :]) ['michael' , 'florence' , 'eli' ]print ('下面是我们的前三名' )for player in players[:3 ]: print (player.title()) 下面是我们的前三名 Charles Martina Michael
复制列表或列表切片
需要注意的是不能直接用列表赋值给新变量,这会使他们指向同一个列表,你操作列表,两个变量都会改变,要复制列表时尽量使用切片复制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 players = ['charles' , 'martina' , 'michael' , 'florence' , 'eli' ] yuwen_win = players[:3 ]print ('语文前三名是:' )print (yuwen_win) 语文前三名是: ['charles' , 'martina' , 'michael' ] shuxue_win = players[-3 :]print ('数学前三名是:' )print (shuxue_win) 数学前三名是: ['michael' , 'florence' , 'eli' ] new_players = playersprint (new_players) ['charles' , 'martina' , 'michael' , 'florence' , 'eli' ] players.append('AAABBB' )print (new_players) ['charles' , 'martina' , 'michael' , 'florence' , 'eli' , 'AAABBB' ]
元组
不可以改变值的列表,称为元组(tuple),元组和列表很像,但是列表是用方括号,而元组是用圆括号。虽然不可以修改元组里面的值,但是可以通过重新赋值改变该变量。
1 2 3 4 5 6 7 8 9 dimensions = (200 , 50 )print (dimensions) (200 , 50 ) dimensions = (400 , 50 )for i in dimensions: print (i)400 50
设置代码格式
为什么要设置代码格式?
代码被阅读的次数远大于编写的次数。在编写完以后的多次阅读中,良好的代码格式可以让阅读花的时间很短。
PEP 8
是设置代码指南,它建议每级缩进4个空格。每个编辑器(IDE)都可以设置,通常(tab)使用是最多的。
每行长度建议不超过79个字符,但这并不是不可逾越的红线,刚开始学的时候不必在意,不过养成这样的习惯后,会对以后和别人合作带来很多方便。
空行建议:不同内容建议用一个空行隔开,不建议使用3、4或多个空行来区隔,主要是空行多了影响阅读。
小结
本章学习了如何高效的处理列表中的元素,如何使用for循环遍历列表,如何创建简单的数值列表以及对数值列表执行的一些操作。还学习了如何使用切片对列表进行操作和复制,最后还学习了元组,以及设置代码格式。
第四章 if语句
编程时经常需要检查一系列的条件,并根据此决定采取什么措施。if语句让你能够检查程序的当前状态,并采取相应的措施。每条if语句的核心都是一个值为True或False的表达式。相应的符号有:==、!=、<、>、<=、>=。关键字有:if、elif、else。在检查多个条件时会用到与、或、非:他们的关键词是:and(两个都为真)、or(一个为真)、not(条件为假)。
示例
1 2 3 4 5 6 7 8 9 10 11 cars = ['audi' , 'bmw' , 'subaru' , 'toyota' ]for car in cars: if car == 'bmw' : print (car.upper()) else : print (car.title()) Audi BMW Subaru Toyota
条件测试
1 2 3 4 5 6 7 8 9 >>> car = 'bmw' >>> car == 'bmw' True >>> car == 'toyota' False >>> car.upper() == 'BMW' True
检查是否不等 ,这里用的符号是 != 。
1 2 3 4 requested_topping = 'mushrooms' if requested_topping != 'anchovies' : print ('Hold the anhovies!' ) Hold the anhovies!
数值比较
1 2 3 4 5 6 7 8 9 10 >>> age = 18 >>> age == 19 False >>> age == 18 True answer = 17 if answer != 42 : print ('这个数值不是42,请再次更改变量值!' ) 这个数值不是42 ,请再次更改变量值!
检查多个条件
1 2 3 4 5 6 7 8 9 >>> age_0 = 21 >>> age_1 = 18 >>> age_0 >=21 and age_1 >=21 False >>> age_0 >=21 or age_1 >=21 True
检查某个特定的值是否在或不在列表中
1 2 3 4 5 6 7 8 9 10 11 >>> number = [1 ,2 ,3 ,4 ,5 ]>>> 5 in numberTrue >>> 9 in numberFalse number = [1 , 2 , 3 , 4 , 5 ] number_0 = 8 if number_0 not in number: print (f'{number_0} 不在我们的数字列表里面。我们把他加进来吧!' )8 不在我们的数字列表里面。我们把他加进来吧!
if 语句
在了解了条件测试之后,就可以编写if
语句了,具体使用哪一种取决于测试的条件数量。
最简单的if 语句
1 2 3 4 age = 18 if age >= 18 : print ('你已经成年了。' ) 你已经成年了。
if-else语句
1 2 3 4 5 6 age = 17 if age >= 18 : print ('你已经成年了。' )else : print ('你还是未成年。' ) 你还是未成年。
if-elif-else语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 age = 12 if age < 4 : print ('你可以免费观看电影。' )elif age < 18 : print ('你可以半价购买电影票。' )else : print ('你需要购买全票。' ) 你可以半价购买电影票。if age < 4 : print ('你可以免费观看电影。' )elif age < 18 : print ('你可以半价购买电影票。' )elif age >= 18 : print ('你需要购买全票。' ) 你可以半价购买电影票。
使用if 语句处理列表
检查列表里面是否有相应的元素
1 2 3 4 5 6 7 8 9 10 11 numbers = [1 , 2 , 3 , 4 , 5 ]for number in numbers: if number == 3 : print ('这个列表里3是中间数' ) else : print (f'这个列表里有{number} .' ) 这个列表里有1. 这个列表里有2. 这个列表里3 是中间数 这个列表里有4. 这个列表里有5.
确定列表是不是空列表 ,列表为空时都是返回False。
1 2 3 4 5 6 7 8 numbers = []if numbers: for number in numbers: print (f'adding {number} ' ) print ('\n所有数字添加了' )else : print ('好像这里面没有数字诶!' ) 好像这里面没有数字诶!
多个列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 numbers_0 = [0 , 1 , 2 , 3 , 4 , 5 ] numbers_1 = [6 , 7 , 8 , 9 , 0 ]print (numbers_0)print (numbers_1)for number in numbers_1: if number in numbers_0: print (f'这两个列表都有 {number} ' ) else : print (f'第一个列表里面没有 {number} 这个数。' ) [0 , 1 , 2 , 3 , 4 , 5 ] [6 , 7 , 8 , 9 , 0 ] 第一个列表里面没有 6 这个数。 第一个列表里面没有 7 这个数。 第一个列表里面没有 8 这个数。 第一个列表里面没有 9 这个数。 这两个列表都有 0
小结
本章学习了if语句以及相关的关键词和符号,也使用for循环遍历列表时对某些元素做特出处理。
第五章 字典
字典就是储存多个有两种相关信息的元素,比如姓名及其年龄,单词及其含义等,字典的信息量也不守限制。
一个简单的字典
1 2 3 4 5 alien = {'color' : 'bule' , 'points' : 5 }print (alien['color' ])print (alien['points' ]) bule5
使用字典
字典(dictionary)是一系列的键值对 ,每个键都对应一个值,字典用放在花括号{
}中的一系列键值对表示。
访问字典中的值
1 2 3 alien = {'color' : 'bule' , 'points' : 5 }print (alien['color' ]) bule
添加键值对
1 2 3 4 alien['x_position' ] = 0 alien['y_position' ] = 25 print (alien) {'color' : 'bule' , 'points' : 5 , 'x_position' : 0 , 'y_position' : 25 }
创建一个空字典
1 2 3 4 5 6 alien = {} alien['color' ] = 'blue' alien['x_position' ] = 0 alien['y_position' ] = 25 print (alien) {'color' : 'blue' , 'x_position' : 0 , 'y_position' : 25 }
修改字典中的值
1 2 3 alien['x_position' ] = 10 print (alien) {'color' : 'blue' , 'x_position' : 10 , 'y_position' : 25 }
删除键值对
1 2 3 4 5 6 7 alien['speed' ] = 'medium' print (alien) {'color' : 'blue' , 'x_position' : 10 , 'y_position' : 25 , 'speed' : 'medium' }del alien['speed' ]print (alien) {'color' : 'blue' , 'x_position' : 10 , 'y_position' : 25 }
由类似对象组成字典
1 2 3 4 5 6 7 8 9 10 11 12 favonlie_languages = { 'jen' : 'python' , 'sarah' : 'c' , 'edward' : 'swift' , 'phil' : 'python' , }for key, value in favonlie_languages.items(): print (f'{key.title()} 喜欢使用{value.title()} 语言编程!' ) Jen喜欢使用Python语言编程! Sarah喜欢使用C语言编程! Edward喜欢使用Swift语言编程! Phil喜欢使用Python语言编程!
使用get()方法来访问值
如果你访问的值不存在字典里,你直接访问的话会出现错误,这时候可以使用get()方法来访问,这个方法可以让你在访问不存在的值的时候返回一个默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 alien = {'color' : 'bule' , 'points' : 5 }print (alien['speed' ]) 发生异常: KeyError'speed' File "favonlie_languages.py" , line 2 , in <module> print (alien['speed' ]) ~~~~~^^^^^^^^^ KeyError: 'speed' print (alien.get('speed' , '不好意思,没有这个索引且没有对应的值' )) 不好意思,没有这个索引且没有对应的值 alien['speed' ] = 'medium' print (alien.get('speed' , '不好意思,没有这个索引且没有对应的值' )) medium
遍历字典
遍历字典中的内容有3个基本的关键词,items()字典中的键和值,有这个方法需要两个临时变量用于遍历字典、keys()遍历字典中的键、values()遍历字典中的值。
遍历所有键值对
1 2 3 4 5 6 7 8 9 10 11 12 like_number = { 'a' : '8' , 'b' : '7' , 'c' : '5' , 'd' : '9' , }for key, value in like_number.items(): print (f'{key.title()} like number is {value} ' ) A like number is 8 B like number is 7 C like number is 5 D like number is 9
遍历所有键
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 favonlie_languages = { 'jen' : 'python' , 'sarah' : 'c#' , 'edward' : 'swift' , 'phil' : 'python' , }for name in favonlie_languages.keys(): print (name.title()) Jen Sarah Edward Philfor name in sorted (favonlie_languages.keys()): print (f'Thank you {name.title()} ,you are welcome!' ) Thank you Edward,you are welcome! Thank you Jen,you are welcome! Thank you Phil,you are welcome! Thank you Sarah,you are welcome!print ('编程语言' )for language in favonlie_languages.values(): print (language) 编程语言 python c swift python
set() 方法用于提取列表或集合中的不同元素,如果有相同的只会提取一个。
1 2 3 4 5 6 7 8 9 10 11 12 print ('编程语言' )for language in set (favonlie_languages.values()): print (language.title()) 编程语言 swift c python>>> number = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,1 ,2 ,3 }>>> number {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }
嵌套
有些时候需要将多个字典储存在列表中或将列表作为值储存在字典中,这中行为就成为嵌套。
字典列表 就是在列表中储存字典可以应用于用户信息等。
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 33 34 35 36 37 38 39 40 alien_0 = {'color' : 'red' , 'points' : 5 } alien_1 = {'color' : 'green' , 'points' : 10 } alien_2 = {'color' : 'blue' , 'points' : 15 } aliens = [alien_0, alien_1, alien_2]for alien in aliens: print (alien) {'color' : 'red' , 'points' : 5 } {'color' : 'green' , 'points' : 10 } {'color' : 'blue' , 'points' : 15 } aliens = []for alien_number in range (30 ): new_alien = {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } aliens.append(new_alien)for alien in aliens[:5 ]: print (alien) {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' }print (f'创建了多少个外星人:{len (aliens)} ' ) 创建了多少个外星人:30 for alien in aliens[:3 ]: if alien['color' ] == 'green' : alien['color' ] = 'yellow' alien['points' ] = 10 alien['speed' ] = 'medium' for alien in aliens[:5 ]: print (alien) {'color' : 'yellow' , 'points' : 10 , 'speed' : 'medium' } {'color' : 'yellow' , 'points' : 10 , 'speed' : 'medium' } {'color' : 'yellow' , 'points' : 10 , 'speed' : 'medium' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' } {'color' : 'green' , 'points' : 5 , 'speed' : 'slow' }
在字典中储存列表 可以用于食品配方等
1 2 3 4 5 6 7 8 9 10 11 pizza = { 'crust' : 'think' , 'toppings' : ['mushrooms' , 'extra cheese' ] }print ( f'You ordered a {pizza["crust" ]} -crust pizza ' "with the following toppings:" )for topping in pizza['toppings' ]: print (f'\t{topping} ' ) You ordered a think-crust pizza with the following toppings: mushrooms extra cheese
在字典中储存字典
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 user = { 'aeinstein' : { 'first' : 'albert' , 'last' : 'einstein' , 'location' : 'princeton' , }, 'mcurie' : { 'first' : 'marie' , 'last' : 'curie' , 'location' : 'paris' , } }for username, user_info in user.items(): print (f'\nUsername: {username} ' ) full_name = f"{user_info['first' ]} {user_info['last' ]} " location = user_info['location' ] print (f'\tFullname: {full_name.title()} ' ) print (f'\tLocation: {location.title()} ' ) Username: aeinstein Fullname: Albert Einstein Location: Princeton Username: mcurie Fullname: Marie Curie Location: Paris
小结
本章我们学习了如何定义字典,以及如何使用储存在字典中的信息。然后学习了如何访问和修改字典中的元素,以及如何遍历字典中的信息。还学习了如何遍历字典中的所有键值对、所有的键和所有的值。学习了如何在列表中嵌套字典,在字典中如何嵌套列表,在字典中嵌套字典。
第六章 用户输入和while循环
input()函数让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其赋给一个变量,以便使用。input()函数接受一个参数,即要向用户显示的提示(prompt)。用户输入的内容Python默认为字符串,要想用户输入其他内容需要再input()前面制定类型,比如:要想用户输入的是数值
int(input("How old are you"))。
注意: 有些文本编辑器不能运行提示用户输入的程序,要运行他们需要从终端运行。在cmd终端里面直接运行python文件就可以了,格式是 :路径>py
文件名和文件后缀。有些python版本需要使用:路径>python
文件名和文件后缀。
1 2 3 4 5 >>> message = input ("tell me something, and I will repeat it back to you:" ) tell me something, and I will repeat it back to you: hello world>>> print (message) hello world>>>
使用int()来获取数值输入
1 2 3 4 5 >>> age = int (input ("How old are you: " )) How old are you: 36 >>> age>20 True
求模运算 是个很有用的工具,它将两个数相除并返回余数。
1 2 3 4 5 6 7 number = int (input ("请输入一个数,我可以判断是奇数还是偶数:" ))if number % 2 == 0 : print ("这个数是偶数!" )else : print ("这个数是奇数!" ) 请输入一个数,我可以判断是奇数还是偶数:50 这个数是偶数!
while 循环简介
for 循环用于针对集合中的每个元素执行一个代码块,而 while
循环这不断地运行,只到指定的条件不再满足为止。
1 2 3 4 5 number = 1 while number <= 5 : print (number, end=" " ) number += 1 1 2 3 4 5
让用户选择何时退出
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 prompt = "我们来玩个游戏,看看你知不知道怎么退出这个游戏: " message = "" while message != "quit" or message != "exit" : message = input (prompt).lower() if message == "quit" or message == "exit" : print ("恭喜你!" ) break 我们来玩个游戏,看看你知不知道怎么退出这个游戏: 2 我们来玩个游戏,看看你知不知道怎么退出这个游戏: 3 我们来玩个游戏,看看你知不知道怎么退出这个游戏: ExiT 恭喜你! prompt = "我们来玩个游戏,看看你知不知道怎么退出这个游戏: " active = True while active: message = input (prompt).lower() if message == "quit" or message == "exit" : print ("恭喜你!" ) active = False else : print ("再想想退出的英文是什么?" ) 我们来玩个游戏,看看你知不知道怎么退出这个游戏: qwe 再想想退出的英文是什么? 我们来玩个游戏,看看你知不知道怎么退出这个游戏: QUIT 恭喜你!
break 退出循环 ,不管条件满不满足,想立即退出 while
循环,不再运行后面的代码,可以使用 break
语句。上面的例子已经使用过一次了。我们再使用一个特别的例子。break
还可以用来退出遍历列表或字典的 for 循环。
1 2 3 4 5 6 7 8 9 10 11 while True : message = input ("请你说说你喜欢哪个城市: " ).lower() if message == "quit" or message == "exit" : print ("再见!" ) break else : print (f"除了{message.title()} 还有其他的城市么?" ) 请你说说你喜欢哪个城市: shang hai 除了Shang Hai 还有其他的城市么? 请你说说你喜欢哪个城市: QUIT 再见!
在循环中使用 continue
要返回循环的开头,并根据条件测试的结果决定是否继续执行循环,可以使用
continue 语句,他不像 break 那样不执行余下的代码并退出整个循环。
1 2 3 4 5 6 7 number = 0 while number < 10 : number += 1 if number % 2 == 0 : continue print (number, end=" " )1 3 5 7 9
避免无线循环
while 循环一定要设置退出条件,如果没有退出条件,while
循环就会无止境的一直执行代码。
1 2 3 number = 0 while number < 5 : print (number)
使用 while
循环处理列表和字典
for 循环是一种遍历列表的有效方式,但不应该在 for
循环中修改列表,否则将导致Python难以跟踪其中的元素。要在遍历列表的同时修改它,可使用
while 循环。通过将 while
循环与列表和字典结合起来使用,可收集、存储并组织大量的输入,供以后查看和使用。注意 ,这里有个有意思的事情,就是列表里面是空的时候它的布尔值是
False ,列表里面有元素的时候,它的布尔值是 True 。
在列表之间移动元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unconfirmed_users = ['admin' , 'zqten' , 'candace' ] confirmed_users = []while unconfirmed_users: current_user = unconfirmed_users.pop() print (f'Verifying user: {current_user.title()} ' ) confirmed_users.append(current_user)print ('\nThe following users have been confirmed:' )for confirmed_user in confirmed_users: print (confirmed_user.title()) Verifying user: Candace Verifying user: Zqten Verifying user: Admin The following users have been confirmed: Candace Zqten Admin
删除为特定值的所有列表元素
1 2 3 4 5 6 7 8 pets = ['dog' , 'cat' , 'dog' , 'goldfish' , 'cat' , 'rabbit' , 'cat' ]print (pets)while 'cat' in pets: pets.remove('cat' )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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 responses = {} polling_active = True print ('这是调查每个人的爱好,请根据提示输入。' )while polling_active: name = input ("请输入被调查者的名字:" ) response = input ("请输入被调查者的回答:" ) responses[name] = response repeat = input ("是否有人继续参与调查?(yes/no)" ) if repeat == 'no' : polling_active = False elif repeat == 'yes' : continue else : while repeat != 'yes' or repeat != 'no' : if repeat == 'no' : polling_active = False break elif repeat == 'yes' : break else : print ("输入错误,请重新输入" ) repeat = input ("是否有人继续参与调查?(yes/no)" ) if repeat == 'no' : polling_active = False print ('\n---调查结果---' )for name, response in responses.items(): print (f"{name} 的爱好是: {response} 。" ) 这是调查每个人的爱好,请根据提示输入。 请输入被调查者的名字:刘翔 请输入被调查者的回答:跨栏 是否有人继续参与调查?(yes/no)不知道 输入错误,请重新输入 是否有人继续参与调查?(yes/no)yes 请输入被调查者的名字:姚明 请输入被调查者的回答:篮球 是否有人继续参与调查?(yes/no)不知道 输入错误,请重新输入 是否有人继续参与调查?(yes/no)no ---调查结果--- 刘翔的爱好是: 跨栏。 姚明的爱好是: 篮球。
小结
本章学习了如何在程序中使用 input()
来让用户提供信息,如何处理文本和数的输入,以及如何使用 while
循环让程序按用户的要求不断地运行。然后见识了多种控制 while
循环流程的方式:设置活动标志,使用 break 语句,以及使用 continue
语句。还学习了如何使用 while
循环在列表之间移动元素,以及如何从列表中删除所有包含特定值的元素。最后,学习了如何结合使用
while 循环和字典。
第七章 函数
函数是带名字的代码块,用于完成具体的工作。要执行函数定义的特定任务,可调用(call)该函数。当需要再程序中多次执行同一项任务时,无须反复编写完成该任务的代码,只需要调用执行该任务的函数,让Python运行其中的代码即可。
定义函数
定义函数的关键字是 def
。后面是函数名和括号,没有参数时可以是空括号,然后和 for 、while
一样也需要冒号,表示定义完成,换行后缩进表示要执行的代码块。
1 2 3 4 5 6 def greet_user (): '''显示简单的问候语''' print ("Hello World!" ) greet_user() Hello World!
向函数传递信息
1 2 3 4 5 6 7 def greet_user (uesrname ): '''显示简单的问候语''' print (f"Hello,{uesrname} !" ) greet_user('zqten' ) Hello,zqten!
实参和形参
在上面的例子中,uesrname是一个形参,即函数完成工作所需要的信息。 而
'zqten' 则是实参,即在调用函数时传递给函数的信息。
传递实参
传递实参有两种方式,一种是按照位置顺序传递,另一种是按照关键字传递。按照关键字传递实参不用考虑顺序,但要记得关键字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def describe_pet (animal_type, pet_name: str ): '''显示宠物信息''' print (f'\nI have a {animal_type} .' ) print (f"My {animal_type} 's name is {pet_name.title()} ." ) describe_pet('cat' , 'duoduo' ) I have a cat. My cat's name is Duoduo. # 按关键字传递实参 describe_pet(pet_name=' nai cha', animal_type=' dog') I have a dog. My dog' s name is Nai Cha.
默认值
有些时候你想改变某个参数,但有些时候你想让这个参数先有一个默认值,后面调用的时候看情况是否修改。这种情况可以为参数设置一个默认值。如果没有设置默认值,在调用的时候也没有传递参数
Python 就会报错。
1 2 3 4 5 6 7 8 9 10 11 12 def describe_pet (animal_type='cat' , pet_name='duo duo' ): '''显示宠物信息''' print (f'\nI have a {animal_type} .' ) print (f"My {animal_type} 's name is {pet_name.title()} ." ) describe_pet() I have a cat. My cat's name is Duo Duo. describe_pet(' dog', ' nai cha') I have a dog. My dog' s name is Nai Cha.
返回值
函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值,函数返回的值称为返回值。在函数中
return
语句将值返回到调用函数的那行代码,返回值能让你将程序的大部分繁重工作移到函数中,从而简化程序。
返回简单的值
1 2 3 4 5 6 7 8 def get_formatted_name (first_name, last_name ): '''返回标准格式的姓名''' full_name = f'{first_name} {last_name} ' return full_name.title() musician = get_formatted_name('jimi' , 'hendrix' )print (musician) Jimi Hendrix
让实参变成可选
就是先设置参数的默认值,但这个默认值是一个空值。不需要的时候就不会出现,需要的时候只需要在调用的时候传递一个参数就可以。
1 2 3 4 5 6 7 8 9 10 11 12 def get_formatted_name (first_name, last_name, middle_name='' ): '''返回标准格式的姓名''' full_name = f'{first_name} {last_name} {middle_name} ' return full_name.title() musician = get_formatted_name('jimi' , 'hendrix' )print (musician) Jimi Hendrix musician = get_formatted_name('jimi' , 'hendrix' , 'YYY' )print (musician) Jimi Hendrix Yyy
返回字典
这里有个小小的细节,就是 age=None 是一个布尔值并且是 False
。意思就是这个参数并没有值返回 False
。如果为这个参数传递了一个值,它就返回 True 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def build_person (first_name, last_name, age=None ): '''返回一个字典,其中包含一个人的信息''' person = {'first' : first_name, 'last' : last_name} if age: person['age' ] = age return person musician = build_person('jimi' , 'hendeix' )print (musician) {'first' : 'jimi' , 'last' : 'hendeix' } musician = build_person('jimi' , 'hendeix' , age=36 )print (musician) {'first' : 'jimi' , 'last' : 'hendeix' , 'age' : 36 }
结合使用函数和 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 def get_formatted_name (first_name, last_name, middle_name='' ): '''返回标准格式的姓名''' full_name = f'{first_name} {last_name} {middle_name} ' return full_name.title()while True : print ('\nPleasr tell me your name:' ) print ("(enter 'q' at any time to quit.)" ) f_name = input ("你姓什么:" ) if f_name == 'q' : break l_name = input ("你的名字是什么:" ) if l_name == 'q' : break full_name = get_formatted_name(f_name, l_name) print (f"你好, {full_name} " ) Pleasr tell me your name: (enter 'q' at any time to quit.) 你姓什么:yao 你的名字是什么:ming 你好, Yao Ming Pleasr tell me your name: (enter 'q' at any time to quit.) 你姓什么:q
传递列表
将列表传递给函数后,函数就能直接访问其内容,进行修改等等操作。
1 2 3 4 5 6 7 8 9 10 11 12 def greet_users (names ): '''向列表中的每个用户发出简单的问候''' for name in names: print ('Hello, %s!' % name) username = ['zqten' , 'zhengjizhong' , 'zhengkainan' , 'zhouhuarong' ] greet_users(username) Hello, zqten! Hello, zhengjizhong! Hello, zhengkainan! Hello, zhouhuarong!
在函数中修改列表 这个程序演示了一个概念:每个函数都应只负责一项具体工作。这有助于将复杂的任务分解成一系列简单的步骤。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 unprinted_designs = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] completed_models = []def print_models (unprinted_designs, completed_models ): ''' 模拟打印每个数字,直到没有未打印的数字为止 打印每个数字后,都将其移到列表completed_models中 ''' while unprinted_designs: current_design = unprinted_designs.pop(0 ) print ('Printing model: %s' % current_design) completed_models.append(current_design)def show_completed_models (completed_models ): '''显示打印好的所有数字''' print ('\nThe following models have been printed:' ) for completed_model in completed_models: print (completed_model, end=' ' ) print_models(unprinted_designs, completed_models) show_completed_models(completed_models)print (unprinted_designs) Printing model: 1 Printing model: 2 Printing model: 3 Printing model: 4 Printing model: 5 Printing model: 6 Printing model: 7 Printing model: 8 Printing model: 9 Printing model: 10 The following models have been printed:1 2 3 4 5 6 7 8 9 10 [] print_models(unprinted_designs[:], completed_models) show_completed_models(completed_models)print (unprinted_designs) Printing model: 1 Printing model: 2 Printing model: 3 Printing model: 4 Printing model: 5 Printing model: 6 Printing model: 7 Printing model: 8 Printing model: 9 Printing model: 10 The following models have been printed:1 2 3 4 5 6 7 8 9 10 [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
传递任意数量的参数
“ * ”
带形参名中的星号让Python创建一个名为形参名的元组,该元组包含函数收到的所有值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def make_pizza (*toppings ): '''比如概述要制作的披萨''' print ('\nMaking a pizza with the following toppings:' ) for topping in toppings: print (topping) make_pizza('aaa' ) make_pizza('mushrooms' , 'extra cheese' ) Making a pizza with the following toppings: aaa Making a pizza with the following toppings: mushrooms extra cheese
结合使用位置实参和任意数量的实参
如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置和关键字实参,再将余下的实参都收集到最后一个形参中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def make_pizza (size, *toppings ): '''比如概述要制作的披萨''' print (f'\nMaking a {size} -inch pizza with the following toppings:' ) for topping in toppings: print (f'- {topping} ' ) make_pizza(16 , 'aaa' ) make_pizza(19 , 'mushrooms' , 'extra cheese' ) Making a 16 -inch pizza with the following toppings: - aaa Making a 19 -inch pizza with the following toppings: - mushrooms - extra cheese
使用任意数量的关键字实参
“ ** ”
带形参名中的两个星号让Python创建一个名为形参名的字典,该字典包含函数收到的所有的键值对。
1 2 3 4 5 6 7 8 9 10 def build_profile (first, last, **user_info ): '''创建一个字典,其中包含我们知道的有关用户的一切''' user_info['first_name' ] = first user_info['last_name' ] = last return user_info user_profile = build_profile( 'zheng' , 'ji zhong' , age=11 , height='147cm' , weight='48kg' )print (user_profile) {'age' : 11 , 'height' : '147cm' , 'weight' : '48kg' , 'first_name' : 'zheng' , 'last_name' : 'ji zhong' }
将函数存储在模块中
将函数存储在称为模块的独立文件中,再将模块导入(import)主程序。可以让代码看起来容易理解。
导入整个模块
要让函数是可导入的,得先创建模块。模块是扩展名为 .py 的文件。
1 2 3 4 5 6 7 8 9 10 import pizza pizza.make_pizza(12 , 'zhu rou' ) pizza.make_pizza(17 , 'niu rou' , 'bai cai' ) Making a 12 -inch pizza with the following toppings: - zhu rou Making a 17 -inch pizza with the following toppings: - niu rou - bai cai
导入特定的函数
只想导入模块中的特定函数。
1 2 3 4 5 6 7 8 9 10 11 from pizza import make_pizza make_pizza(12 , 'zhu rou' ) make_pizza(16 ,'niu rou' , 'bai cai' ) Making a 12 -inch pizza with the following toppings: - zhu rou Making a 16 -inch pizza with the following toppings: - niu rou - bai cai
使用 as 给函数指定别名
如果要导入的函数的名称太长或者可能与程序中的其他名称有冲突,可指定简短而独一无二的别名。这个方法也可以应用到给模块指定别名。
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 from pizza import make_pizza as mp mp(12 , 'zhu rou' ) mp(16 , 'niu rou' , 'bai cai' , 'fanqie' ) Making a 12 -inch pizza with the following toppings: - zhu rou Making a 16 -inch pizza with the following toppings: - niu rou - bai cai - fanqieimport pizza as p p.make_pizza(12 , 'zhu rou' ) p.make_pizza(16 , 'niu rou' , 'bai cai' , 'fanqie' ) Making a 12 -inch pizza with the following toppings: - zhu rou Making a 16 -inch pizza with the following toppings: - niu rou - bai cai - fanqie
导入模块中的所有函数
这种方法一般慎用,因为如果模块中有函数的名称与当前项目中的名称相同,可能导致意想不到的结果。最好的做法是要么只导入需要使用的函数,要么导入整个模块并使用点号调用 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pizza import * make_pizza(12 , 'zhu rou' ) make_pizza(16 , 'niu rou' , 'bai cai' , 'fanqie' ) Making a 12 -inch pizza with the following toppings: - zhu rou Making a 16 -inch pizza with the following toppings: - niu rou - bai cai - fanqiefrom 文件夹名称 import 模块from 文件夹名称.模块 import 函数
函数编写指南
在编写函数时需要牢记几个细节。
应给函数指定描述性的名称,且只使用小写字母和下划线。
每个函数都应包含简要阐述其功能的注释。意思就是每个函数都需要编写使用说明的注释。
形参指定默认值时,等号两边不要有空格。
如果程序或模块包含多个函数,可以使用两个空行将函数隔开。
所有的 import
语句都应该放在文件开头。唯一的例外是,你要在文件开头编写整个程序的注释。
导入自己边学的模块时,尽量把模块和程序放在一个文件夹。
小结
本章学习了如何编写函数,以及如何传递实参,让函数能够访问完成工作所需的信息。然后学习了如何使用位置实参和关键字实参,以及如何接受任意数量的实参,学习了显示输出的函数和返回值的函数,知道了如何将函数与列表、字典、if、语句和
while
循环结合起来使用,以及如何将函数存储在称为模块的独立文件中,让程序文件更简单、更易于理解。最后,了解了函数编写指南,遵循这些指南可让程序始终保持良好的结构。
程序员的目标之一是编写简单的代码来完成任务,而函数有助于实现这样的目标。
第八章 类
面向对象编程(object-oriented-programming,
OOP)是最有效的软件编写方法之一。在基于类创建对象时,每个对象都自动具备类定义的通用行为。然后,你可根据需要赋予每个对象独特的个性。根据类来创建对象称为实例化 ,这让你能够使用类的实例。
面相对象变成有助于你像程序员那样看世界,并且真正明白自己编写的代码:不仅是各行代码的作用,还有代码背后更宏大的概念。了解类背后的概念可培养逻辑思维能力,让你能够通过编写程序来解决遇到的几乎任何问题。
创建和使用类
创建Dog类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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." )
“init ()”方法是类的初始化方法,类中的函数称为方法。这个方法的开头和结尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
这个方法定义成包含三个形参:self 、name、age。这个方法中self
是必不可少,而且必须位于其他形参的前面。self就是类的自身,当我们实例化这个类时,我们传递的参数会通过
self 传递给类自身。self.name = name 里面 self
前缀的变量可供类中的所有方法使用。
根据类创建实例
可以将类视为有关如何创建实例的说明。:yum:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 my_dog = Dog('duoduo' , 7 )print (f"My dog's name is {my_dog.name} " )print (f"My dog's age is {my_dog.age} " ) My dog's name is duoduo My dog' s age is 7 my_dog.sit() my_dog.roll_over() duoduo is now sitting. duoduo rolled over. your_dog = Dog('Lucy' , 4 )print (f"Your dog's name is {your_dog.name} " )print (f"Your dog's age is {your_dog.age} " ) your_dog.sit() Your dog's name is Lucy Your dog' s age is 4 Lucy is now sitting.
使用类和实例
Car 类
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class Car : '''模拟汽车的简单尝试''' def __init__ (self, make, model, year ): '''初始化描述汽车属性''' self .make = make self .model = model self .year = year self .odometer = 0 self .oli = 240 def get_descriptive_name (self ): '''返回格式规范的描述性信息''' long_name = f"{self.year} 年 {self.make} 生产{self.model} " return long_name.title() def read_odometer (self ): '''返回汽车里程数''' print (f"这两车已经行驶了{self.odometer} 公里了。" ) return self .odometer def update_odometer (self, mileage ): '''设置汽车里程数''' if mileage >= self .odometer: self .odometer = mileage print (f"这辆车已经行驶了{self.odometer} 公里了。" ) else : print ("你不能往回设置公里数" ) def increment_odometer (self, mileage ): '''增加汽车里程数''' self .odometer += mileage print (f"这辆车已经行驶了{self.odometer} 公里了。" ) def car_oli_v (self, oli ): '''描述车辆油箱的容积''' self .oli = oli print (f'这辆车的油箱容积是{self.oli} L.' ) my_new_car = Car('奥迪汽车公司' , '奥迪A4' , '2003' )print (my_new_car.get_descriptive_name()) my_new_car.odometer = 1 my_new_car.read_odometer() my_new_car.update_odometer(34 ) my_new_car.increment_odometer(12 ) my_new_car.update_odometer(12 )2003 年奥迪汽车公司生产奥迪A4 这两车已经行驶了1 公里了。 这两车已经行驶了34 公里了。 这辆车已经行驶了46 公里了。 你不能往回设置公里数
继承
编写类的时候并非总是要从头开始,如果要编写的类是一个已经存在的类的特殊版本,可以使用继承 。当一个类继承另一个类时,将自动获得后者的素有属性和方法。原有类称为父类 ,而新的类称为子类 。子类不仅继承了父类的所有属性和方法,还可以定义自己的属性和方法。
子类初始化方法
1 2 3 4 5 6 7 8 9 10 11 12 class ElectricCar (Car ): '''模拟电动汽车''' def __init__ (self, make, model, year ): '''初始化父类属性,在初始化电动汽车特有的属性''' super ().__init__(make, model, year) my_leaf = ElectricCar('nissan' , 'leaf' , '2024' )print (my_leaf.get_descriptive_name())2024 年Nissan生产Leaf
给子类定义属性和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class ElectricCar (Car ): '''模拟电动汽车''' def __init__ (self, make, model, year ): '''初始化父类属性,在初始化电动汽车特有的属性''' super ().__init__(make, model, year) self .battery_size = 40 def describe_battery (self ): '''打印一条描述电池容量的消息''' print (f'This car has a {self.battery_size} -KWh battery.' ) my_leaf = ElectricCar('nissan' , 'leaf' , '2024' )print (my_leaf.get_descriptive_name()) my_leaf.describe_battery()2024 年Nissan生产Leaf This car has a 40 -KWh battery.
重写父类中的方法
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 class ElectricCar (Car ): '''模拟电动汽车''' def __init__ (self, make, model, year ): '''初始化父类属性,在初始化电动汽车特有的属性''' super ().__init__(make, model, year) self .battery_size = 40 def describe_battery (self ): '''打印一条描述电池容量的消息''' print (f'This car has a {self.battery_size} -KWh battery.' ) def car_oli_v (self ): '''电动汽车没有油箱''' print ("电动汽车没有油箱" ) my_leaf = ElectricCar('nissan' , 'leaf' , '2024' )print (my_leaf.get_descriptive_name()) my_leaf.describe_battery() my_leaf.car_oli_v()2024 年Nissan生产Leaf This car has a 40 -KWh battery. 电动汽车没有油箱
将实例用作属性
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 class ElectricCar (Car ): '''模拟电动汽车''' def __init__ (self, make, model, year ): '''初始化父类属性,在初始化电动汽车特有的属性''' super ().__init__(make, model, year) self .battery = Battery() def car_oli_v (self ): '''电动汽车没有油箱''' print ("电动汽车没有油箱" )class Battery : def __init__ (self, battery_size=40 ): '''初始化电池的属性''' self .battery_size = battery_size def describe_battery (self ): '''打印一条描述电池容量的消息''' print (f'This car has a {self.battery_size} -KWh battery.' ) my_leaf = ElectricCar('nissan' , 'leaf' , '2024' )print (my_leaf.get_descriptive_name()) my_leaf.battery.describe_battery()2024 年Nissan生产Leaf This car has a 40 -KWh battery.
导入类
主要目的是让文件整洁,我们可以将类存储在模块中,然后在主程序导入所需的模块。我们需要整理一下代码,把上面我们学的三个类都复制到一个文件,命名为
car.py 。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 class Car : '''模拟汽车的简单尝试''' def __init__ (self, make, model, year ): '''初始化描述汽车属性''' self .make = make self .model = model self .year = year self .odometer = 0 def get_descriptive_name (self ): '''返回格式规范的描述性信息''' long_name = f"{self.year} 年{self.make} 生产{self.model} " return long_name.title() def read_odometer (self ): '''返回汽车里程数''' print (f"这辆车已经行驶了{self.odometer} 公里了。" ) return self .odometer def update_odometer (self, mileage ): '''设置汽车里程数''' if mileage >= self .odometer: self .odometer = mileage print (f"这辆车已经行驶了{self.odometer} 公里了。" ) else : print ("你不能往回设置公里数" ) def increment_odometer (self, mileage ): '''增加汽车里程数''' self .odometer += mileage print (f"这辆车已经行驶了{self.odometer} 公里了。" ) class Battery : def __init__ (self, battery_size=40 , car_model_isoli=False ): '''初始化电池的属性''' self .battery_size = battery_size '''默认是电动汽车,如果是汽油车可以设置会Ture.''' self .car_model_isoli = car_model_isoli def describe_battery (self ): '''打印一条描述电池容量的消息''' if self .car_model_isoli is True : print ('这是一辆油车,没有电池.' ) else : print (f'This car has a {self.battery_size} -KWh battery.' ) def update_battery (self ): '''升级电池容量''' if self .battery_size != 65 : self .battery_size = 65 def get_range (self ): '''打印一条消息,指出汽车的续航里程''' if self .battery_size == 40 : range = 150 elif self .battery_size == 65 : range = 225 print (f'这辆车的续航里程为{range } 公里.' )class ElectricCar (Car ): '''模拟电动汽车''' def __init__ (self, make, model, year ): '''初始化父类属性,在初始化电动汽车特有的属性''' super ().__init__(make, model, year) self .battery = Battery()
导入单个类
导入多个类
1 from car import Car, ElectricCar
导入整个模块
这里我把上面的代码分成了两个文件,Car类单独储存成 car.py
,电池模组和电动车储存为 electric_car.py 。
1 2 3 4 5 6 7 import car my_car = car.Car("柳州五菱" , "五菱之光" , 2008 )from car import *
使用别名
就是给导入的模块或类更改使用名,这并不会影响该模块和类的代码。
1 2 3 4 import electric_car as ecfrom electric_car import ElectricCar as EC
合适的工作流程
首先尝试在一个文件中完成所有工作,确定一切都能正确运行后,在将类移到独立的模块中。
这里讲一下 VMC 模式:
V: 就是视窗,用户界面等可视化的代码。
M:就是模块或类等等的代码。
C:就是整个程序的运行逻辑,流程控制等等的代码。
这个模式可以简单的理解为建立三个文件夹,分别存放这三种类型的文件。这样可以让自己的代码更加简洁高效,别人理解也更容易。
Python标准库
Python标准库是一组模块,在安装Python时已经包含在内了。我们可以使用标准可中的任何函数和类。查看Python标准库,可以在命令行输入
help("modules") 命令。
1 2 3 4 5 6 7 8 9 >>> from random import randint>>> randint(1 ,6 )4 >>> from random import choice >>> players = ['aaa' ,'bbb' ,'ccc' ,'ddd' ]>>> choice(players)'ccc'
类的编程风格
编写复杂程序时采用以下几项:
类名:驼峰命名法。例:MyNewCar 。类名不使用下划线。
模块名和实例名都采用全小写格式,并在单词之间采用下划线。
每个类都要在定义后面和函数一样需要描述类功能的文档字符串。
当需要导入标准库中的模块和自己编写的模块时,优先导入标准库中的模块,在导入自己编写的模块。
小结
本章我们学习了如何编写类,如何使用属性在类中存储的信息,以及如何编写方法让类具备所需的行为。然后学习了
init
初始化方法。了解了如何修改实例的属性,包括直接修改以及通过方法修改。还了解到使用继承可简化相关类的创建工作,将一个类的实例用作另一个类的属性能让类更简洁。
明白了,通过将类存储在模块(文件)中,并在需要使用这些类的文件中导入它们,可让项目变的更简洁。开始了解python标准库,还看了一个random模块,最后学习了编写类时应遵循的Python约定。
第九章 文件和异常
处理文件,让程序能够快速地分析大量数据;错误处理,避免程序在面对意外情况时崩溃;异常是Python创建特殊对象,用于管理程序运行时出现的错误;还将学习使用
json 模块保存用户数据,以免这些数据在程序结束运行后丢失。
读取文件
读取文件对数据分析应用程序很有用。要使用文本文件中的信息,首先需要将信息读取到内存中。既可以一次性读取文件的全部内容,也可以逐行读取。
读取文件的全部内容
1 2 3 4 # 这是一个txt文件 3.1415926535 8979323846 2643383279
1 2 3 4 5 6 7 8 9 10 11 12 from pathlib import Path path = Path("E:/text_files/pi_digits.txt" ) contents = path.read_text()print (contents)3.1415926535 8979323846 2643383279
相对文件路径和绝对文件路径
相对文件路径 让 Python
到相对于当前运行的程序所在目录的指定位置去查找。比如上面的文件可以这样读取
1 2 from pathlib import Path path = Path("text_files/pi_digits.txt" )
绝对文件路径 可以读取系统中任何地方的文件。
1 2 from pathlib import Path path = Path("E:/text_files/pi_digits.txt" )
现在最简单的做法是,要么将数据文件存储在程序文件所在的目录中,要么将其存储在存续文件所在目录下的一个文件夹中。
注意 :在显示文件路径时,windows 系统使用反斜杠(
)而不是斜杠( / )但是你在代码中应该始终使用斜杠( /
),即便在windows系统中也是如此。在与你或其他用户的系统交互时,pathlib
库会自动使用正确的路径表示方法。
访问文件中的各行
使用 splitlines()
方法可以将字符串转换为一系列行,在使用 for
循环遍历文件中的每一行,splitlines()
方法返回一个列表,其中包含文件中所有的行。可以把这个列表赋值给变量。
1 2 3 4 5 6 7 8 9 10 11 12 from pathlib import Path path = Path("E:/text_files/pi_digits.txt" ) contents = path.read_text() lines = contents.splitlines()for line in lines: print (line)3.1415926535 8979323846 2643383279
使用文件的内容
读取文件后才能使用这些数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pathlib import Path path = Path("E:/text_files/pi_digits.txt" ) contents = path.read_text() pi_string = '' lines = contents.splitlines()for line in lines: pi_string += lineprint (pi_string)print (len (pi_string))3.1415926535 8979323846 2643383279 36 --snip--for line in lines: pi_string += line.lstrip()print (pi_string)print (len (pi_string))3.141592653589793238462643383279 32
注意 :读取文本文件时,python
将其中的所有文本都解释为字符串。如果读取的是数,并且要将其作为数字使用,就必须使用
int() 函数将其转换为整数,或者使用 float() 函数将其转为浮点数。
大型文件 可以做切片等列表的操作。
1 2 3 4 5 6 7 8 --snip--for line in lines: pi_string += line.lstrip()print (pi_string[:10 ])print (len (pi_string))3.14159265 32
趣味小练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pathlib import Path path = Path("pi_million_digits.txt" ) contents = path.read_text() pi_string = '' lines = contents.splitlines()for line in lines: pi_string += line.strip() birthday = input ("你的生日如(20200506):" )if birthday in pi_string: print ("圆周率包含了你的生日" )else : print ("圆周率没有你的生日哦..." ) 你的生日如(20200506 ):20200506 圆周率没有你的生日哦...
写入文件
保存数据的最简单的方式之一是将其写入文件。
写入一行
注意 :在python调用 write_text()
方法时,如果指定文件已存在,这个方法会将其内容替换 为你要写入的内容。replace() 方法可以将字符串中的特定单词替换为另一个单词。
1 2 3 4 5 6 7 8 9 10 11 from pathlib import Path path = Path('programming.txt' ) path.write_text("hello world" ) programming.txt hello world>>> a = 'How are you?' >>> a.replace('you' ,'me' )'How are me?'
注意 :Python
只能将字符串写入文本,如果要将数值数据存储到文本文件中,须使用 str()
函数将其转换为字符串格式。
写入多行
1 2 3 4 5 6 7 8 9 10 11 from pathlib import Path path = Path('programming.txt' ) contents = "nihao" contents += "\nwohao" contents += "\ndajiahao" path.write_text(contents) programming.txt nihao wohao dajiahao
趣味小练习
1 2 3 4 5 6 7 8 9 10 from pathlib import Path path = Path("guest.txt" ) n = 0 c = '' while n != 5 : b = input ("请输入内容:" ) c += f"{b} \n" n += 1 path.write_text(f"{c} " , encoding='utf-8' )
异常
异常是使用 try-except
代码块处理的。也就是说你运行一段代码,如过出错了会根据你编写的代码执行,如果没有对异常进行处理,程序出错了就会停止。如果你运行一段代码出错,编辑器会提示你是什么错误,你可以针对这个错误给出处理方法,就像下面的
0 不能作为除数的错误是:ZeroDivisionError
1 2 print (5 /0 ) ZeroDivisionError: division by zero
使用 try-except 代码块
当你认为可能会发生错误的时候,可以使用这个代码块来处理可能引发的错误。
1 2 3 4 5 6 try : print (5 /0 )except ZeroDivisionError: print ("0不能作为除数。" ) 0 不能作为除数。
处理 FileNotFoundError 异常
1 2 3 4 5 6 7 8 9 from pathlib import Path path = Path("alice.txt" )try : contents = path.read_text(encoding='utf-8' )except FileNotFoundError: print ("没有找到这个文件。" ) 没有找到这个文件。
分析文本
split() 方法是把一个很长的字符串转换成很多单词的列表的一个方法。
1 2 3 4 5 6 7 8 9 10 11 12 from pathlib import Path path = Path("alice.txt" )try : contents = path.read_text(encoding='utf-8' )except FileNotFoundError: print ("没有找到这个文件。" )else : words = contents.split() num_words = len (words) print (f"The file {path} has about {num_words} words" )
读取多个文件
利用函数我们可以读取多个文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pathlib import Pathdef count_words (path ): try : contents = path.read_text(encoding='utf-8' ) except FileNotFoundError: print (f"没有找到{path} 这个文件。" ) else : words = contents.split() num_words = len (words) print (f"The file {path} has about {num_words} words" ) filenames = ['alice.txt' , 'little_women.txt' , 'moby_dick.txt' , 'kkk.txt' ]for filename in filenames: path = Path(filename) count_words(path) The file alice.txt has about 29594 words The file little_women.txt has about 189142 words The file moby_dick.txt has about 215864 words 没有找到kkk.txt这个文件。
静默失败
并非每次错误都需要告诉用户,有时候有错误什么也不做,但是就是让程序继续运行可以使用
pass 语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def count_words (path ): try : contents = path.read_text(encoding='utf-8' ) except FileNotFoundError: pass else : words = contents.split() num_words = len (words) print (f"The file {path} has about {num_words} words" ) filenames = ['alice.txt' , 'little_women.txt' , 'moby_dick.txt' , 'kkk.txt' ]for filename in filenames: path = Path(filename) count_words(path) The file alice.txt has about 29594 words The file little_women.txt has about 189142 words The file moby_dick.txt has about 215864 words
存储数据
json
模块能够将简单的Python数据结构转换为JSON格式的字符串,并在程序再次运行时从文件中加载数据。
注意: JSON格式最初是为 JavaScript
开发的,但后来成为了一种通用格式,被众多语言采用。
json.dumps() 和 json.loads()
json.dumps()
接受一个参数,即要转换为JSON格式的数据。这个函数返回一个字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pathlib import Pathimport json number = [1 , 2 , 3 , 5 , 6 , 7 , 4 , 8 , 9 ] path = Path('number.json' ) contents = json.dumps(number) path.write_text(contents) [1 , 2 , 3 , 5 , 6 , 7 , 4 , 8 , 9 ]from pathlib import Pathimport json path = Path('number.json' ) contents = path.read_text() number = json.loads(contents)print (number) [1 , 2 , 3 , 5 , 6 , 7 , 4 , 8 , 9 ]
保存和读取用户生成的数据
保存数据很有必要,因为如果不以某种方式进行存储,用户的信息就会在程序停止运行时丢失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pathlib import Pathimport json username = input ("请输入你的名字:" ) path = Path('username.json' ) contents = json.dumps(username) path.write_text(contents, encoding='utf-8' )print ("我们将储存你的名字。" ) 请输入你的名字:zqten 我们将储存你的名字。"zqten" from pathlib import Pathimport json path = Path("username.json" ) contents = path.read_text() username = json.loads(contents)print (f"Welcome back {username} " ) Welcome back zqten
Path
类提供了很多有用的方法。如果指定的文件或文件夹存在,exists()
方法返回 True ,否则返回 False 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pathlib import Pathimport json path = Path('username.json' )if path.exists(): contents = path.read_text() username = json.loads(contents) print (f"Welcome back {username} " )else : username = input ("请输入你的名字:" ) contents = json.dumps(username) path.write_text(contents, encoding='utf-8' ) print (f"我们将储存你的名字。{username} " ) Welcome back zqten
重构
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 33 34 35 36 from pathlib import Pathimport jsondef get_stored_username (path ): '''如果用户存储了用户名,我们就获取他''' if path.exists(): contents = path.read_text() username = json.loads(contents) return username else : return None def get_new_username (path ): '''提示用户输入用户名''' username = input ("请输入你的名字:" ) contents = json.dumps(username) path.write_text(contents, encoding='utf-8' ) return usernamedef greet_user (): '''问候用户,并指出其名字''' path = Path('username.json' ) username = get_stored_username(path) if username: print (f"Welcome back {username} " ) else : username = get_new_username(path) print (f"我们将储存你的名字,{username} " ) greet_user() 请输入你的名字:zqten 我们将储存你的名字,zqten greet_user() Welcome back zqten
小结
本章学习了如何使用文件,包括如何读取整个文件,如何读取文件中的各行,以及如何根据需要将任意数量的文本写入文件。然后学习了异常,以及如何处理程序可能引发的异常。最后学习了如何存储Python数据结构,以保存用户提供的信息,避免让用户在每次运行程序时都重新提供。
第十章 测试代码
本章使用的是pip安装的pytest库来进行测试代码。
1 2 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest
测试函数
测试基本分为下面几种:
单元测试,用于核实函数的某个方面没有问题。
测试用例,是一组单元测试,核实函数在各种情况下的行为都符合要求。
全覆盖,测试一整套单元测试,涵盖了各种可能的函数使用方式。
测试函数,需要新建一个文件,导入需要测试的函数,然后定义一个测试函数(这个函数的命名规范是:必须以
test
加下划线打头)。在测试过程中,pytest 会找出并运行所有以
test
加下划线打头的函数。运行测试,需要在终端进入到要测试的程序的文件夹,输入pytest就会出现下面的测试内容。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 def get_formatted_name (frist, last, middle='' ): if middle: full_name = f"{frist} {middle} {last} " else : full_name = f"{frist} {last} " return full_name.title()from name_function import get_formatted_namedef test_frist_last_name (): formatted_name = get_formatted_name('janis' , 'joplin' ) assert formatted_name == 'Janis Joplin' ======================== test session starts ======================== platform win32 -- Python 3.11 .3 , pytest-7.4 .0 , pluggy-1.3 .0 rootdir: E:Python\第十章测试代码 collected 1 item test_name_function.py . [100 %] ======================== 1 passed in 0.01 s ========================== ===================== test session starts ============================ platform win32 -- Python 3.11 .3 , pytest-7.4 .0 , pluggy-1.3 .0 rootdir: E:Python\第十章测试代码 collected 1 item test_name_function.py F [100 %] ====================== FAILURES ====================================== __________________ test_frist_last_name_______________________________ def test_frist_last_name (): formatted_name = get_formatted_name('janis' , 'joplin' ) > assert formatted_name == 'Janis Joplil' E AssertionError: assert 'Janis Joplin' == 'Janis Joplil' E - Janis Joplil E ? ^ E + Janis Joplin E ? ^ test_name_function.py:6 : AssertionError ============= short test summary info =========================================== FAILED test_name_function.py: :test_frist_last_name - AssertionError: assert 'Janis Joplin' == 'Janis Joplil' ============= 1 failed in 0.05 s =================================================
测试类
上面是测试了函数,现在我们针对类进行测试。
各种断言:
assert a == b 断言两个值相等
assert a != b 断言两个值不等
这里只列出了两个,测试能包含任意可用条件语句表示的断言,比如 not
、in等等。
要测试的类
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 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 ("所有问卷结果:" ) for response in self .responses: print (f"- {response} " )from survey import AnonymousSurvey question = "你学习了几种语言?" language_survey = AnonymousSurvey(question) language_survey.show_question()print ("按'q'退出。\n" )while True : response = input ("输入你学习的语言: " ) if response == "q" : break language_survey.store_response(response)print ("\n你学习的语言有" ) language_survey.show_results() 你学习了几种语言? 按'q' 退出。 输入你学习的语言: 汉语 输入你学习的语言: 英语 输入你学习的语言: 日语 输入你学习的语言: 德语 输入你学习的语言: q 你学习的语言有 所有问卷结果: - 汉语 - 英语 - 日语 - 德语
测试AnonymousSurvey类
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 33 34 35 36 37 38 39 from survey import AnonymousSurveydef test_store_single_response (): '''测试单个答案会被妥善地存储''' question = "What language did you first learn to speak?" language_survey = AnonymousSurvey(question) language_survey.store_response('English' ) assert 'English' in language_survey.responses ==================== test session starts ============================== platform win32 -- Python 3.11 .3 , pytest-7.4 .0 , pluggy-1.3 .0 rootdir: E:Python\第十章测试代码 collected 1 items test_survey.py . [100 %] ===================== 1 passed in 0.02 s =============================== def test_store_three_response (): '''测试多个答案会被妥善地存储''' question = "What language did you first learn to speak?" language_survey = AnonymousSurvey(question) responses = ['English' , 'Spanish' , 'Chinese' ] for response in responses: language_survey.store_response(response) for response in responses: assert response in language_survey.responses ========================= test session starts =============================== platform win32 -- Python 3.11 .3 , pytest-7.4 .0 , pluggy-1.3 .0 rootdir: E:Python\第十章测试代码 collected 2 items test_survey.py .. [100 %] ========================= 2 passed in 0.02 s =================================
使用夹具
夹具(@pytest.fixture )可帮助我们搭建测试环境,用于测试多个项目。这个需要导入(import
pytest)。夹具使用方法是放在函数定义前面的指令。要使用夹具时,可编写一个函数来生成供多个测试函数使用的资源再对这个函数应用装饰器@pytest.fixture,并让使用该资源的每个测试函数都接受一个与该函数同名的形参。
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 import pytestfrom survey import AnonymousSurvey@pytest.fixture def language_survey (): '''一个可供所有测试函数使用的AnonymousSurvey实例''' question = "What language did you first learn to speak?" language_survey = AnonymousSurvey(question) return language_surveydef test_store_single_response (language_survey ): '''测试单个答案会被妥善地存储''' language_survey.store_response('English' ) assert 'English' in language_survey.responsesdef test_store_three_response (language_survey ): '''测试多个答案会被妥善地存储''' responses = ['English' , 'Spanish' , 'Chinese' ] for response in responses: language_survey.store_response(response) for response in responses: assert response in language_survey.responses
小结
本章学习了如何使用 pytest
模块中的工具来为函数和类编写测试。不仅学习了如何编写测试函数,以核实函数和类的行为符合预期,而且学习了如何使用夹具来高效地创建可在测试文件中的多个测试函数中使用的资源。
第二部分 项目
第十二章 武装飞船
外星人入侵项目规划
玩家控制着一艘武装飞船出现在屏幕底部中央,玩家可以使用方向键左右移动飞船,使用空格键进行射击。
当游戏开始时,一个外形舰队出现在天空中,并向屏幕下方移动。
玩家的任务是消灭这些外星人。
玩家将万星人消灭干净后,将出现一个新的外形舰队,其移动速度更快。
只要有万星人撞到玩家的飞船或到达屏幕下边缘,玩家就损失一艘飞船。玩家损失三艘飞船游戏结束。
安装Pygame
开始游戏项目
创建 Pygame 窗口及响应用户输入
这里有及个新方法:
pygame.display.set_mode((1200,800)) 设置显示画面的大小
pygame.display.set_caption("Alien Invasion") 设置标题和logo
pygame.display.flip() 让渲染的可见
pygame.time.Clock() 设置游戏帧率
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 import sysimport pygameclass AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" pygame.init() self .screen = pygame.display.set_mode((1200 ,800 )) pygame.display.set_caption("Alien Invasion" ) def run_game (self ): """开始游戏主循环""" while True : for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() pygame.display.flip()if __name__ == '__main__' : ai = AlienInvasion() ai.run_game()
控制帧率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" pygame.init() self .clock = pygame.time.Clock() --snip-- def run_game (self ): """开始游戏主循环""" while True : --snip-- pygame.display.flip() self .clock.tick(60 )
设置背景颜色
fill() 方法是填充背景颜色,该方法只接受一个表示颜色的实参。
1 2 3 4 5 6 7 8 9 10 11 12 def __init__ (self ): --snip-- pygame.display.set_caption("Alien Invasion" ) self .bg_color = (230 ,230 ,230 ) def run_game (self ): """开始游戏主循环""" --snip-- self .screen.fill(self .bg_color) pygame.display.flip()
创建 Settings 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Settings : """存储游戏《外星人入侵》中所有设置的类""" def __init__ (self ): """初始化游戏的设置""" self .screen_width = 1200 self .screen_height = 800 self .screen_color = (230 , 230 , 230 )class AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" --snip-- self .screen = pygame.display.set_mode((self .settings.screen_width, self .settings.screen_height)) def run_game (self ): """开始游戏主循环""" --snip-- self .screen.fill(self .settings.screen_color)
添加飞船图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import pygameclass Ship : """管理飞船类""" def __init__ (self, ai_game ): """初始化飞船并设置其初始位置""" self .screen = ai_game.screen self .screen_rect = ai_game.screen.get_rect() self .image = pygame.image.load('images/ship.bmp' ) self .rect = self .image.get_rect() self .rect.midbottom = self .screen_rect.midbottom def blitme (self ): """在指定位置绘制飞船""" self .screen.blit(self .image, self .rect)
注意: 在 pygame
中,原点(0,0)位于屏幕左上角,当一个点向右下方移动时,它的坐标值将增大,在1200X800的屏幕上,原点位于左上角,右下角的坐标为(1200,800)。这些坐标对应的是游戏窗口,而不是物理屏幕。
在屏幕上绘制飞船
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from ship import Shipclass AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" --snip-- pygame.display.set_caption("Alien Invasion" ) self .ship = Ship(self ) def run_game (self ): """开始游戏主循环""" --snip-- self .screen.fill(self .settings.screen_color) self .ship.blitme()
重构:_check_events()
方法和 _update_screen() 方法
在Python中辅助方法的名称以单下划线打头
_ check_events() 方法和 _update_screen()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def run_game (self ): """开始游戏主循环""" while True : self ._check_events() self ._update_screen() self .clock.tick(60 )def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit()def _update_screen (self ): self .screen.fill(self .settings.screen_color) self .ship.blitme() pygame.display.flip()
驾驶飞船
响应按键
pygame中,事件都是通过 pygame.event.get() 方法获取的。
1 2 3 4 5 6 7 8 9 10 --snip-- def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: self .ship.rect.x += 1
持续移动
pygame.KEYDOWN 和 pygame.KEYUP 两个事件是,按下键盘,和释放键盘。
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 33 34 35 36 37 38 39 def run_game (self ): """开始游戏主循环""" while True : self ._check_events() self .ship.update() self ._update_screen() self .clock.tick(60 ) def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: self .ship.moving_right = True elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT: self .ship.moving_right = False class Ship : """管理飞船类""" def __init__ (self, ai_game ): """初始化飞船并设置其初始位置""" --snip-- self .moving_right = False def update (self ): """根据移动标志调整飞船的位置""" if self .moving_right: self .rect.x += 1 --snip--
左右移动
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 33 class Ship : """管理飞船类""" --snip-- self .moving_right = False self .moving_left = False def update (self ): """根据移动标志调整飞船的位置""" if self .moving_right: self .rect.x += 1 if self .moving_left: self .rect.x -= 1 --snip-- class AlienInvasion : """管理游戏资源和行为的类""" def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: self .ship.moving_right = True if event.key == pygame.K_LEFT: self .ship.moving_left = True elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT: self .ship.moving_right = False if event.key == pygame.K_LEFT: self .ship.moving_left = False
调整飞船的速度
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 class Settings : """存储游戏《外星人入侵》中所有设置的类""" def __init__ (self ): """初始化游戏的设置""" --snip-- self .ship_speed = 1.5 class Ship : """管理飞船类""" def __init__ (self, ai_game ): """初始化飞船并设置其初始位置""" --snip-- self .x = float (self .rect.x) def update (self ): """根据移动标志调整飞船的位置""" if self .moving_right: self .x += self .settings.ship_speed if self .moving_left: self .x -= self .settings.ship_speed self .rect.x = self .x
限制飞船的活动范围
1 2 3 4 5 6 7 8 9 10 class Ship : """管理飞船类""" --snip-- def update (self ): """根据移动标志调整飞船的位置""" if self .moving_right and self .rect.right < self .screen_rect.right: self .x += self .settings.ship_speed if self .moving_left and self .rect.left > 0 : self .x -= self .settings.ship_speed
**重构:_check_events() 方法**
检查事件方法越来越长,我们将其部分代码放在两个方法中,一个处理键盘按下(KEYDOWN),一个处理键盘释放(KEYUP)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: self ._check_keydown_events(event) elif event.type == pygame.KEYUP: self ._check_keyup_events(event)def _check_keydown_events (self, event ): """响应按下键盘""" if event.key == pygame.K_RIGHT: self .ship.moving_right = True elif event.key == pygame.K_LEFT: self .ship.moving_left = True def _check_keyup_events (self, event ): """响应释放键盘""" if event.key == pygame.K_RIGHT: self .ship.moving_right = False if event.key == pygame.K_LEFT: self .ship.moving_left = False
按 Q 退出
1 2 3 4 5 6 7 8 def _check_keydown_events (self, event ): """响应按下键盘""" if event.key == pygame.K_RIGHT: self .ship.moving_right = True elif event.key == pygame.K_LEFT: self .ship.moving_left = True elif event.key == pygame.K_q: sys.exit()
在全屏模式下运行游戏
在创建屏幕时,传入(0, 0), pygame.FULLSCREEN
,这让pygame生成一个覆盖整个显示器的屏幕。由于无法知道屏幕的宽度和高度,所有后面接着要更新屏幕的
rect 的属性宽和高来更新对象 settings
。pygame不提供全屏模式下退出游戏的默认方式,所以运行前,确保可以使用 'q'
退出
1 2 3 4 5 6 7 8 9 class AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" --snip-- self .screen = pygame.display.set_mode((0 , 0 ), pygame.FULLSCREEN) self .settings.screen_width = self .screen.get_rect().width self .settings.screen_height = self .screen.get_rect().height
简单回顾
下面将添加射击功能,所以需要新增一个名为 bullet.py
的文件,并修改一些原有的文件,在添加其他功能前,先回顾一下这些文件,以便对这个项目的组织结构有清楚的认识。
alien_invasion.py 这个文件包含 AlienInvasion
类,这个类创建在游戏的很多地方会用到的一系列属性。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 import sysimport pygamefrom settings import Settingsfrom ship import Shipfrom bullet import Bulletclass AlienInvasion : """管理游戏资源和行为的类""" def __init__ (self ): """初始化游戏并创建游戏资源""" pygame.init() self .clock = pygame.time.Clock() self .settings = Settings() self .screen = pygame.display.set_mode((self .settings.screen_width, self .settings.screen_height)) pygame.display.set_caption("Alien Invasion" ) self .ship = Ship(self ) self .bullets = pygame.sprite.Group() def run_game (self ): """开始游戏主循环""" while True : self ._check_events() self .ship.update() self ._update_bullets() self ._update_screen() self .clock.tick(60 ) def _check_events (self ): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: self ._check_keydown_events(event) elif event.type == pygame.KEYUP: self ._check_keyup_events(event) def _check_keydown_events (self, event ): """响应按下键盘""" if event.key == pygame.K_RIGHT: self .ship.moving_right = True elif event.key == pygame.K_LEFT: self .ship.moving_left = True elif event.key == pygame.K_q: sys.exit() elif event.key == pygame.K_SPACE: self ._fire_bullet() def _check_keyup_events (self, event ): """响应释放键盘""" if event.key == pygame.K_RIGHT: self .ship.moving_right = False if event.key == pygame.K_LEFT: self .ship.moving_left = False def _fire_bullet (self ): """创建一颗子弹,并将其加入编著bullets """ if len (self .bullets) < self .settings.bullet_allowed: new_bullet = Bullet(self ) self .bullets.add(new_bullet) def _update_bullets (self ): """更新子弹的位置并删除已消失的子弹""" self .bullets.update() for bullet in self .bullets.copy(): if bullet.rect.bottom <= 0 : self .bullets.remove(bullet) def _update_screen (self ): self .screen.fill(self .settings.screen_color) for bullet in self .bullets.sprites(): bullet.draw_bullet() self .ship.blitme() pygame.display.flip()if __name__ == '__main__' : ai = AlienInvasion() ai.run_game()
settings.py 这个文件包含 Settings
类,这个类只有一个方法,这个文件主要用于初始化控制游戏外观和飞船速度的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Settings : """存储游戏《外星人入侵》中所有设置的类""" def __init__ (self ): """初始化游戏的设置""" self .screen_width = 1200 self .screen_height = 800 self .screen_color = (230 , 230 , 230 ) self .ship_speed = 1.5 self .bullet_speed = 2.0 self .bullet_width = 3 self .bullet_height = 15 self .bullet_color = (60 , 60 , 60 ) self .bullet_allowed = 5
ship.py 这个文件包含 Ship 类,这个类主要用于在屏幕上绘制飞船
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 33 34 35 36 37 38 39 40 import pygameclass Ship : """管理飞船类""" def __init__ (self, ai_game ): """初始化飞船并设置其初始位置""" self .screen = ai_game.screen self .settings = ai_game.settings self .screen_rect = ai_game.screen.get_rect() self .image = pygame.image.load('images/ship.bmp' ) self .rect = self .image.get_rect() self .rect.midbottom = self .screen_rect.midbottom self .x = float (self .rect.x) self .moving_right = False self .moving_left = False def update (self ): """根据移动标志调整飞船的位置""" if self .moving_right and self .rect.right < self .screen_rect.right: self .x += self .settings.ship_speed if self .moving_left and self .rect.left > 0 : self .x -= self .settings.ship_speed self .rect.x = self .x def blitme (self ): """在指定位置绘制飞船""" self .screen.blit(self .image, self .rect)
射击
bullett.py 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 import pygamefrom pygame.sprite import Spriteclass Bullet (Sprite ): """管理飞船所发射子弹的类""" def __init__ (self, ai_game ): """在飞船的当前位置创建一个子弹对象""" super ().__init__() self .screen = ai_game.screen self .settings = ai_game.settings self .color = self .settings.bullet_color self .rect = pygame.Rect(0 , 0 , self .settings.bullet_width, self .settings.bullet_height) self .rect.midtop = ai_game.ship.rect.midtop self .y = float (self .rect.y) def update (self ): """向上移动子弹""" self .y -= self .settings.bullet_speed self .rect.y = self .y def draw_bullet (self ): """在屏幕上绘制子弹""" pygame.draw.rect(self .screen, self .color, self .rect)
小结
本章学习了游戏开发计划的指定以及使用Pygame编写的游戏的基本结构。接着学习了如何设置背景色,以及如何将设置存储在独立的类中。然后学习了如何在屏幕上绘制图像,以及如何让玩家控制游戏元素的移动。不仅创建了能自动移动的元素,还删除了不再需要的对象。最后学习了经常性重构是如何为项目的后续开发提供便利的。
第十三章 外星人
项目回顾
本章将完成下列开发:
创建第一个外星人
小结
本章通过创建外星舰队学习了如何在游戏中添加大量相同的元素,如何使用嵌套循环来创建成行成列的整齐元素,以及如何通过调用每个元素的
update()
方法移动大量的元素。接着学习了如何控制对象在屏幕上的移动方向,以及如何响应特定的情形,如有外星人到达屏幕边缘。然后学习了如何检测并相应子弹和外星人的碰撞以及外星人和飞创的碰撞。最后学习了如何在游戏中跟踪统计信息,以及如何使用标志
game_active
来判断游戏是否结束。