前言
Life is short. You need Python.
Bruce Eckel上边这句话是Python社区的名言,翻译过来就是人生苦短,我用Python。我和Python结缘于一次服务器的调试,从此便一发不可收拾。我从来没有遇到一门编程语言可以如此干净、简洁,如果你有处女座情节,你一定会爱上这门语言。使用Python,可以说是很难写出丑陋的代码。我从来没想过一门编程语言可以如此简单,它太适合零基础的朋友踏入编程的大门了,如果我有一个八岁的孩子,我一定会毫不犹豫地使用Python引导他学习编程,因为面对它,永远不缺乏乐趣。Python虽然简单,其设计却十分严谨。尽管Python可能没有C或C这类编译型语言运行速度那么快,但是C和C需要你无时无刻地关注数据类型、内存溢出、边界检查等问题。而Python,它就像一个贴心的仆人,私底下为你都一一处理好,从来不用你操心这些,这让你可以将全部心思放在程序的设计逻辑之上。有人说,完成相同的一个任务,使用汇编语言需要1000行代码,使用C语言需要500行,使用Java只需要100行,而使用Python,可能只要20行就可以了。这就是Python,使用它来编程,你可以节约大量编写代码的时间。既然Python如此简单,会不会学了之后没什么实际作用呢?事实上你并不用担心这个问题,因为Python可以说是一门万金油语言,在Web应用开发、系统网络运维、科学与数字计算、3D游戏开发、图形界面开发、网络编程中都有它的身影。目前越来越多的IT企业,在招聘栏中都有精通Python语言优先考虑的字样。另外,就连Google都在大规模使用Python。好了,我知道过多的溢美之词反而会使大家反感,所以我必须就此打住,剩下的就留给大家自己体验吧。接下来简单地介绍一下这本书。一年前,出版社的编辑老师无意间看到了我的一个同名的教学视频,建议我以类似的风格撰写一本书。当时我是受宠若惊的,也很兴奋。刚开始写作就遇到了不小的困难如何将视频中口语化的描述转变为文字。当然,我希望尽可能地保留原有的幽默和风趣毕竟学习是要快乐的。这确实需要花不少时间去修改,但我觉得这是值得的。本书不假设你拥有任何一方面的编程基础,所以本书不但适合有一定编程基础,想学习Python3的读者,也适合此前对编程一无所知,但渴望用编程改变世界的朋友!本书提倡理解为主,应用为王。因此,只要有可能,我都会通过生动的实例来让大家理解概念。虽然这是一本入门书籍,但本书的野心可并不止于初级水平的教学。本书前半部分是基础的语法特性讲解,后半部分围绕着Python3在爬虫、Tkinter和游戏开发等实例上的应用。编程知识深似海,没办法仅通过一本书将所有的知识都灌输给你,但我能够做到的是培养你对编程的兴趣,提高你编写代码的水平,以及锻炼你的自学能力。最后,本书贯彻的核心理念是: 实用、好玩,还有参与。本书对应的系列视频教程,可以在http:blog.fishc.comcategorypython下载得到,也可扫描以下二维码关注微信号进行观看。
编者2016年7月
第5章列表、元组和字符串
5.1列表: 一个打了激素的数组有时候需要把一堆东西暂时存储起来,因为它们有某种直接或者间接的联系,需要把它们放在某种组或者集合中,因为将来可能用得上。很多接触过编程的朋友都知道或者听说过数组。数组这个概念呢,就是把一大堆同种类型的数据挨个儿摆在一块儿,然后通过数组下标进行索引。但数组有一个基本要求,就是你所放在一起的数据必须类型一致。由于Python的变量没有数据类型,也就是说,Python是没有数组的。但是呢,Python加入了更为强大的列表。Python的列表有多强大?如果把数组比作是一个集装箱的话,那么Python的列表就是一个工厂的仓库了。列表真的非常有用,基本上所有的Python程序都要使用到列表,包括之前的打飞机游戏,里边的小飞机可以全部扔到一个列表中统一管理。5.1.1创建列表创建列表和创建普通变量一样,用中括号括起一堆数据就可以了,数据之间用逗号隔开,这样一个普普通通的列表就创建成功了:
number = [1, 2, 3, 4, 5]
我们说列表是打了激素的数组不是没有道理的,可以创建一个鱼龙混杂的列表:
mix = [1, "小甲鱼", 3.14, [1, 2, 3]]
可以看到上边这个列表里有整型、字符串、浮点型数据,甚至还可以包含着另一个列表。当然,如果实在想不到要往列表里边塞什么数据的时候,可以先创建一个空列表:
empty = []
5.1.2向列表添加元素列表相当灵活,所以它的内容不可能总是固定的,现在就来教大家如何向列表添加元素吧。要向列表添加元素,可以使用append方法:
number = [1, 2, 3, 4, 5]
number.append6
number
[1, 2, 3, 4, 5, 6]
可以看到,参数6已经被添加到列表number的末尾了。有读者可能会问,这个方法调用怎么跟平时的BIF内置函数调用不一样呢?嗯,因为append不是一个BIF,它是属于列表对象的一个方法。中间这个.,大家暂时可以理解为范围的意思: append这个方法是属于一个叫作number的列表对象的。关于对象的知识,咱暂时只需要理解这么多,后边再给大家介绍对象。同理,我们可以把数字7和8添加进去,但是我们发现似乎不能用append同时添加多个元素:
number.append7, 8
Traceback most recent call last:
File "pyshell#122", line 1, in module
number.append7, 8
TypeError: append takes exactly one argument 2 given
这时候就可以使用extend方法向列表末尾添加多个元素:
number.extend7, 8
Traceback most recent call last:
File "pyshell#123", line 1, in module
number.extend7, 8
TypeError: extend takes exactly one argument 2 given
哎呀,怎么又报错了呢?!嗯,其实小甲鱼是故意的。extend方法事实上使用一个列表来扩展另一个列表,所以它的参数应该是一个列表:
number.extend[7, 8]
number
[1, 2, 3, 4, 5, 6, 7, 8]
好,我们又再一次向世界证明我们成功了!但是又发现了一个问题,到目前为止,我们都是往列表的末尾添加数据,那如果我想插队呢?当然没问题,想要往列表的任意位置插入元素,就要使用insert方法。insert方法有两个参数: 第一个参数代表在列表中的位置,第二个参数是在这个位置处插入一个元素。不妨来试一下,让数字0出现在列表number的最前边:
number.insert1, 0
number
[1, 0, 2, 3, 4, 5, 6, 7, 8]
等等,不是说好插入到第一个位置吗?怎么插入后0还是排在1的后边呢?其实是这样的: 凡是顺序索引,Python均从0开始,同时这也是大多数编程语言约定俗成的规范。那么大家知道为什么要用0来表示第一个数吗?是因为计算机本身就是二进制的,在二进制的世界里只有两个数: 0和1,当然,0就是二进制里的第一个数了,所以嘛,秉承着这样的传统,0也就习惯用于表示第一个数。所以,正确的做法应该是:
number = [1, 2, 3, 4, 5, 6, 7, 8]
number.insert0, 0
number
[0, 1, 2, 3, 4, 5, 6, 7, 8]
5.1.3从列表中获取元素跟数组一样,可以通过元素的索引值index从列表获取单个元素,注意,列表索引值是从0开始的:
name = ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]
name[0]
''鸡蛋''
name[3]
''李狗蛋''
那按照这个方法让李狗蛋和鸭蛋的位置互调:
name[1], name[3] = name[3], name[1]
name
[''鸡蛋'', ''李狗蛋'', ''鹅蛋'', ''鸭蛋'']
5.1.4从列表删除元素从列表删除元素,这里也介绍三种方法: remove、del和pop。先演示一下用remove删除元素:
name.remove"李狗蛋"
name
[''鸡蛋'', ''鹅蛋'', ''鸭蛋'']
使用remove删除元素,你并不需要知道这个元素在列表中的具体位置,只需要知道该元素存在列表中就可以了。如果要删除的东西根本不在列表中,程序就会报错:
name.remove"陈鸭蛋"
Traceback most recent call last:
File "pyshell#138", line 1, in module
name.remove"陈鸭蛋"
ValueError: list.removex: x not in list
remove方法并不能指定删除某个位置的元素,这时就要用del来实现:
del name[1]
name
[''鸡蛋'', ''鸭蛋'']
注意,del是一个语句,不是一个列表的方法,所以你不必在它后边加上小括号。另外,如果你想删除整个列表,还可以直接用del加列表名删除:
del name
name
Traceback most recent call last:
File "pyshell#142", line 1, in module
name
NameError: name ''name'' is not defined
最后,演示用pop方法弹出元素:
name = ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]
name.pop
''李狗蛋''
name.pop
''鹅蛋''
name
[''鸡蛋'', ''鸭蛋'']
大家看到了,pop方法默认是弹出列表中的最后一个元素。但这个pop方法其实还可以灵活运用,当你为它加上一个索引值作为参数的时候,它会弹出这个索引值对应的元素:
name = ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]
name.pop2
''鹅蛋''
name
[''鸡蛋'', ''鸭蛋'', ''李狗蛋'']
5.1.5列表分片利用索引值,每次可以从列表获取一个元素,但是人总是贪心的,如果需要一次性获取多个元素,有没有办法实现呢?利用列表分片slice,可以方便地实现这个要求:
name = ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]
name[0:2]
[''鸡蛋'', ''鸭蛋'']
很简单对吧?只不过是用一个冒号隔开两个索引值,左边是开始位置,右边是结束位置。这里要注意的一点是,结束位置上的元素是不包含的。利用列表分片,得到一个原来列表的拷贝,原来列表并没有发生改变。列表分片也可以简写,我们说过Python就是以简洁闻名于世,所以你能想到的便捷方案,Python的作者以及Python社区的小伙伴们都已经想到了,并付诸实践,你要做的就是验证一下是否可行:
name[:2]
[''鸡蛋'', ''鸭蛋'']
name[1:]
[''鸭蛋'', ''鹅蛋'', ''李狗蛋'']
name[:]
[''鸡蛋'', ''鸭蛋'', ''鹅蛋'', ''李狗蛋'']
如果没有开始位置,Python会默认开始位置是0。同样道理,如果要得到从指定索引值到列表末尾的所有元素,把结束位置省去即可。如果没有放入任何索引值,而只有一个冒号,将得到整个列表的拷贝。再一次强调: 列表分片就是建立原列表的一个拷贝或者说副本,所以如果你想对列表做出某些修改,但同时还想保持原来的那个列表,那么直接使用分片的方法来获取拷贝就很方便了。5.1.6列表分片的进阶玩法分片操作实际上还可以接收第三个参数,其代表的是步长,默认情况下不指定它的时候该值为1,来试试将其改成2会有什么效果?
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list1[0:9:2]
[1, 3, 5, 7, 9]
如果将步长改成2,那么每前进两个元素才取一个出来。其实还可以直接写成list1[::2]。如果步长的值是负数,例如-1,结果会怎样呢?不妨试试看:
list1[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
是不是很有意思?这里步长设置为-1,就相当于复制一个反转的列表。5.1.7一些常用操作符
此前学过的大多数操作符都可以运用到列表上:
list1 = [123]
list2 = [234]
list1 list2
False
list3 = [''abc'']
list4 = [''bcd'']
list3 list4
True
我们发现列表还是挺聪明的,竟然会懂得比较大小。那如果列表中不止一个元素呢?结果又会如何?
list1 = [123, 456]
list2 = [234, 123]
list1 list2
False
怎么会这样?list1列表的和是123 456=579,按理应该比list2列表的和234 123=357要大,为什么list1list2还会返回False呢?思考片刻后得出结论: Python的列表原来并没有我们想象中那么智能注: 在后边讲魔法方法的章节会教大家如何把列表改变得更加聪明,当列表包含多个元素的时候,默认是从第一个元素开始比较,只要有一个PK赢了,就算整个列表赢了。字符串比较也是同样的道理字符串比较的是第一个字符对应的ASCII码值的大小。我们知道字符串可以用加号 来进行拼接,用乘号*来复制自身若干次。它们在列表身上也是可以实现的:
list1 = [123, 456]
list2 = [234, 123]
list3 = list1 list2
list3
[123, 456, 234, 123]
加号 也叫连接操作符,它允许我们把多个列表对象合并在一起,其实就相当于extend方法实现的效果。一般情况下建议大家使用extend方法来扩展列表,因为这样显得更为规范和专业。另外,连接操作符并不能实现列表添加新元素的操作:
list1 = [123, 456]
list2 = list1 789
Traceback most recent call last:
File "pyshell#177", line 1, in module
list2 = list1 789
TypeError: can only concatenate list not "int" to list
所以如果要添加一个元素到列表中,用什么方法?嗯,可以用append或者insert方法,希望大家还记得。乘号*也叫重复操作符,重复操作符可以用于列表中:
list1 = [123]
list1 * 3
[123, 123, 123]
当然复合赋值运算符也可以用于列表:
list1 *= 5
list1
[123, 123, 123, 123, 123]
另外有个成员关系操作符大家也不陌生,我们是在谈for循环的时候认识它的,成员关系操作符就是in和not in!
list1 = ["小猪", "小猫", "小狗", "小甲鱼"]
"小甲鱼" in list1
True
"小护士" not in list1
True
之前说过列表里边可以包含另一个列表,那么对于列表中的列表元素,能不能使用in和not in测试呢?试试便知:
list1 = ["小猪", "小猫", ["小甲鱼", "小护士"], "小狗"]
"小甲鱼" in list1
False
"小护士" not in list1
True
可见,in和not in只能判断一个层次的成员关系,这跟break和continue语句只能跳出一个层次的循环是一个道理。那要判断列表里边的列表的元素,应该先进入一层列表:
"小甲鱼" in list1[2]
True
"小护士" not in list1[2]
False
顺便说一下,前面提到使用索引号去访问列表中的值,那么对于列表中的值,应该如何访问呢?大家应该猜到了,其实跟C语言访问二维数组的方法相似:
list1[2][0]
''小甲鱼''
5.1.8列表的小伙伴们接下来认识一下列表的小伙伴们,那么列表有多少小伙伴呢?不妨让Python自己告诉我们:
dirlist
[''__add__'', ''__class__'', ''__contains__'', ''__delattr__'', ''__delitem__'', ''__dir__'', ''__doc__'', ''__eq__'', ''__format__'', ''__ge__'', ''__getattribute__'', ''__getitem__'', ''__gt__'', ''__hash__'', ''__iadd__'', ''__imul__'', ''__init__'', ''__iter__'', ''__le__'', ''__len__'', ''__lt__'', ''__mul__'', ''__ne__'', ''__new__'', ''__reduce__'', ''__reduce_ex__'', ''__repr__'', ''__reversed__'', ''__rmul__'', ''__setattr__'', ''__setitem__'', ''__sizeof__'', ''__str__'', ''__subclasshook__'', ''append'', ''clear'', ''copy'', ''count'', ''extend'', ''index'', ''insert'', ''pop'', ''remove'', ''reverse'', ''sort'']
产生了一个熟悉又陌生的列表,很多熟悉的方法似曾相识,例如append、extend、insert、pop、remove都是学过的。现在再给大家介绍几个常用的方法。count这个方法的作用是计算它的参数在列表中出现的次数:
list1 = [1, 1, 2, 3, 5, 8, 13, 21]
list1.count1
2
index这个方法会返回它的参数在列表中的位置:
list1.index1
0
可以看到,这里是返回第一个目标1在list1中的位置,index方法还有两个参数,用于限定查找的范围。因此可以这样查找第二个目标在list1的位置:
start = list1.index1 1
stop = lenlist1
list1.index1, start, stop
1
reverse方法的作用是将整个列表原地翻转,就是排最后的放到最前边,排最前的放到最后,那么排倒数第二的就排在第二,以此类推:
list1 = [1, 2, 3, 4, 5, 6, 7, 8]
list1.reverse
list1
[8, 7, 6, 5, 4, 3, 2, 1]
sort这个方法是用指定的方式对列表的成员进行排序,默认不需要参数,从小到大排队:
list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0]
list1.sort
list1
[0, 1, 2, 3, 5, 6, 8, 9, 10]
那如果需要从大到小排队呢?很简单,先调用sort方法,列表会先从小到大排好队,然后调用reverse方法原地翻转就可以啦。什么?太麻烦?好吧,大家真是越来越懒了很好,大家离天才又近了一步小甲鱼个人认为懒是创新发明的根源和动力。。其实,sort这个方法其实有三个参数,其形式为sortfunc, key, reverse。func和key参数用于设置排序的算法和关键字,默认是使用归并排序,算法问题不在这里讨论,有兴趣的朋友可以看一下小甲鱼另一本不错的教程: 《数据结构和算法》C语言。这里要讨论sort方法的第三个参数: reverse,没错,就是刚刚学的那个reverse方法的那个reverse。不过这里作为sort的一个默认参数,它的默认值是sortreverse=False,表示不颠倒顺序。因此,只需要把False改为True,列表就相当于从大到小排序:
list1 = [8, 9, 3, 5, 2, 6, 10, 1, 0]
list1.sortreverse=True
list1
[10, 9, 8, 6, 5, 3, 2, 1, 0]
5.1.9关于分片拷贝概念的补充上一节提到使用分片创建列表的拷贝:
list1 = [1, 3, 2, 9, 7, 8]
list2 = list1[:]
list2
[1, 3, 2, 9, 7, 8]
list3 = list1
list3
[1, 3, 2, 9, 7, 8]
看似一样,对吧?但事实上呢?利用列表的一个小伙伴做以下修改,大家看看差别:
list1.sort
list1
[1, 2, 3, 7, 8, 9]
list2
[1, 3, 2, 9, 7, 8]
list3
[1, 2, 3, 7, 8, 9]
可以看到list1已经从小到大排好了序,那list2和list3呢?使用分片方式得到的list2很有原则、很有格调,并不会因为list1的改变而改变,这个原理我待会儿跟大家说; 接着看list3看,真正的墙头草是list3,它竟然跟着list1改变了,这是为什么呢?不知道大家还记不记得在讲解变量的时候说过,Python的变量就像一个标签,就一个名字而已还是给大家画个图好理解,如图51所示。
图51拷贝列表
这下大家应该明白了吧,为一个列表指定另一个名字的做法,只是向同一个列表增加一个新的标签而已,真正的拷贝是要使用分片的方法。这个也是初学者最容易混淆的地方,大家以后写代码时一定要注意哦。
5.2元组: 戴上了枷锁的列表早在三百多年前,孟德斯鸠在《轮法的精神》里边就提到一切拥有权力的人都容易滥用权力,这是万古不变的一条经验。但是凡是拥有大权力的人,都想用自身的实践证明孟德斯鸠是一个只会说屁话的家伙,但是他们好像都失败了由于列表过分强大,Python的作者觉得这样似乎不妥,于是发明了列表的表亲元组。元组和列表最大的区别就是你可以任意修改列表中的元素,可以任意插入或者删除一个元素,而对元组是不行的,元组是不可改变的像字符串一样,所以你也别指望对元组进行原地排序等高级操作了。5.2.1创建和访问一个元组元组和列表,除了不可改变这个显著特征之外,还有一个明显的区别是,创建列表用的是中括号,而创建元组大部分时候用的是小括号注意,我这里说的是大部分:
tuple1 = 1, 2, 3, 4, 5, 6, 7, 8
tuple1
1, 2, 3, 4, 5, 6, 7, 8
访问元组的方式与列表无异:
tuple1[1]
2
tuple1[5:]
6, 7, 8
tuple1[:5]
1, 2, 3, 4, 5
也使用分片的方式来复制一个元组:
tuple2 = tuple1[:]
tuple2
1, 2, 3, 4, 5, 6, 7, 8
如果你试图修改元组的一个元素,那么抱歉,Python会很不开心:
tuple1[1] = 1
Traceback most recent call last:
File "pyshell#7", line 1, in module
tuple1[1] = 1
TypeError: ''tuple'' object does not support item assignment
我很好奇如果问你,列表的标志性符号是中括号[],那么元组的标志性符号是什么?你会怎么回答呢?小甲鱼相信百分之九十的朋友都会不假思索地回答: 小括号啊,有部分比较激进的朋友还可能会补充一句小甲鱼你傻啊?好吧,这个问题其实也是大部分初学者所忽略和容易上当的,我们实验一下:
temp = 1
typetemp
class ''int''
还记得type方法吧,作用是返回参数的类型,这里它返回说temp变量是整型int。再试试:
temp = 1, 2, 3
typetemp
class ''tuple''
噢,发现了吧?就算没有小括号,temp还是元组类型,所以逗号,才是关键,小括号只是起到补充的作用。但是你如果想要创建一个空元组,那么你就直接使用小括号即可:
temp =
typetemp
class ''tuple''
所以这里要注意的是,如果要创建的元组中只有一个元素,请在它后边加上一个逗号,,这样可以明确告诉Python你要的是一个元组,不要拿什么整型、浮点型来忽悠你:
temp1 = 1
typetemp1
class ''int''
temp2 = 1,
typetemp2
class ''tuple''
temp3 = 1,
typetemp3
class ''tuple''
为了证明逗号,起到了决定性作用,再给大家举个例子:
8 * 8
64
8 * 8,
8, 8, 8, 8, 8, 8, 8, 8
5.2.2更新和删除元组有朋友可能会说,刚才不是你自己说元组是板上钉钉不能修改的吗?你现在又来谈更新一个元组,小甲鱼你这不是自己打脸吗?大家不要激动我们只是讨论一个相对含蓄的做法直接在同一个元组上更新是不可行的,除非你学习了后边的魔法方法章节。不知道大家还记不记得以前是如何更新一个字符串的?没错,是通过拷贝现有的字符串片段构造一个新的字符串的方式解决的,对元组也是使用同样的方法:
temp = "小鸡", "小鸭", "小猪"
temp = temp[:2] "小甲鱼", temp[2:]
temp
''小鸡'', ''小鸭'', ''小甲鱼'', ''小猪''
上面的代码需要在小鸭和小猪中间插入小甲鱼,那么通过分片的方法让元组拆分为两部分,然后再使用连接操作符 合并成一个新元组,最后将原来的变量名temp指向连接好的新元组。不妨可以把这样的做法称为狸猫换太子。在这里就要注意了,逗号是必需的,小括号也是必需的!在谈到列表的时候,小甲鱼跟大家说有三个方法可以删除列表里边的元素,但是对于元组是不可变的原则来说,单独删除一个元素是不可能的,当然你可以用刚才小甲鱼教给大家更新元组的方法,间接地删除一个元素:
temp = temp[:2] temp[3:]
temp
''小鸡'', ''小鸭'', ''小猪''
如果要删除整个元组,只要使用del语句即可显式地删除一个元组:
del temp
temp
Traceback most recent call last:
File "pyshell#30", line 1, in module
temp
NameError: name ''temp'' is not defined
其实在日常使用中,很少使用del去删除整个元组,因为Python的回收机制会在这个元组不再被使用到的时候自动删除。最后小结一下哪些操作符可以使用在元组上,拼接操作符和重复操作符刚刚演示过了,关系操作符、逻辑操作符和成员关系操作符in和not in也可以直接应用在元组上,这跟列表是一样的,大家自己实践一下就知道了。关于列表和元组,我们今后会谈得更多,目前,就先聊到这里。
5.3字符串或许现在又回过头来谈字符串,有些朋友可能会觉得没必要。其实关于字符串,还有很多你可能不知道的秘密,由于字符串在日常使用中是如此常见,因此小甲鱼抱着负责任的态度在本节把所知道的都倒出来跟大家分享一下。关于创建和访问字符串,前面已经介绍过了。不过学了列表和元组,我们知道了分片的概念,事实上也可以应用于字符串之上:
str1 = "I love fishc.com!"
str1[:6]
''I love''
接触过C语言的朋友应该知道,在C语言中,字符串和字符是两个不同的概念C语言用单引号表示字符,双引号表示字符串。但在Python并没有字符这个类型,在Python看来,所谓字符,就是长度为1的字符串。当要访问字符串的其中一个字符的时候,只需用索引列表或元组的方法来索引字符串即可:
str1[5]
''e''
字符串跟元组一样,都是属于一言既出、驷马难追的家伙。所以一旦定下来就不能直接对它们进行修改了,如果必须要修改,我们就需要委曲求全
str1[:6] " 插入的字符串" str1[6:]
''I love 插入的字符串 fishc.com!''
但是大家要注意,这种通过拼接旧字符串的各个部分得到新字符串的方式并不是真正意义上的改变原始字符串,原来的那个家伙还在,只是将变量指向了新的字符串旧的字符串一旦失去了变量的引用,就会被Python的垃圾回收机制释放掉。像比较操作符、逻辑操作符、成员关系操作符等的操作跟列表和元组是一样的,这里就不再啰唆了。5.3.1各种内置方法列表和元组都有它们的方法,大家可能觉得列表的方法已经非常多了,其实字符串更多呢。表51总结了字符串的所有方法及对应的含义。
表51Python字符串的方法
方法含义
capitalize把字符串的第一个字符改为大写
casefold把整个字符串的所有字符改为小写
centerwidth将字符串居中,并使用空格填充至长度width的新字符串
countsub[, start[, end]]返回sub在字符串里边出现的次数,start和end参数表示范围,可选
encodeencoding=''utf8'', errors=''strict''以encoding指定的编码格式对字符串进行编码
endswithsub[, start[, end]]检查字符串是否以sub子字符串结束,如果是返回True,否则返回False。start和end参数表示范围,可选
expandtabs[tabsize=8]把字符串中的tab符号\t转换为空格,如不指定参数,默认的空格数是tabsize=8
findsub[, start[, end]]检测sub是否包含在字符串中,如果有则返回索引值,否则返回-1,start和end参数表示范围,可选续表
方法含义
indexsub[, start[, end]]跟find方法一样,不过如果sub不在string中会产生一个异常
isalnum如果字符串至少有一个字符并且所有字符都是字母或数字则返回True,否则返回False
isalpha如果字符串至少有一个字符并且所有字符都是字母则返回True,否则返回False
isdecimal如果字符串只包含十进制数字则返回True,否则返回False
isdigit如果字符串只包含数字则返回True,否则返回False
islower如果字符串中至少包含一个区分大小写的字符,并且这些字符都是小写,则返回True,否则返回False
isnumeric如果字符串中只包含数字字符,则返回True,否则返回False
isspace如果字符串中只包含空格,则返回True,否则返回Falseistitle如果字符串是标题化所有的单词都是以大写开始,其余字母均小写,则返回True,否则返回Falseisupper如果字符串中至少包含一个区分大小写的字符,并且这些字符都是大写,则返回True,否则返回Falsejoinsub以字符串作为分隔符,插入到sub中所有的字符之间ljustwidth返回一个左对齐的字符串,并使用空格填充至长度为width的新字符串lower转换字符串中所有大写字符为小写lstrip去掉字符串左边的所有空格partitionsub找到子字符串sub,把字符串分成一个3元组pre_sub, sub, fol_sub,如果字符串中不包含sub则返回''原字符串'', '' '', '' ''replaceold, new[, count]把字符串中的old子字符串替换成new子字符串,如果count指定,则替换不超过count次rfindsub[, start[, end]]类似于find方法,不过是从右边开始查找rindexsub[, start[, end]]类似于index方法,不过是从右边开始查找rjustwidth返回一个右对齐的字符串,并使用空格填充至长度为width的新字符串rpartitionsub类似于partition方法,不过是从右边开始查找rstrip删除字符串末尾的空格splitsep=None, maxsplit=-1不带参数默认是以空格为分隔符切片字符串,如果maxsplit参数有设置,则仅分隔maxsplit个子字符串,返回切片后的子字符串拼接的列表splitlines[keepends]按照''\n''分隔,返回一个包含各行作为元素的列表,如果keepends参数指定,则返回前keepends行startswithprefix[, start[, end]]检查字符串是否以prefix开头,是则返回True,否则返回False。start和end参数可以指定范围检查,可选strip[chars]删除字符串前边和后边所有的空格,chars参数可以定制删除的字符,可选swapcase翻转字符串中的大小写title返回标题化所有的单词都是以大写开始,其余字母均小写的字符串translatetable根据table的规则可以由str.maketrans''a'', ''b''定制转换字符串中的字符upper转换字符串中的所有小写字符为大写zfillwidth返回长度为width的字符串,原字符串右对齐,前边用0填充。
这里选几个常用的给大家演示一下用法,首先是casefold方法,它的作用是将字符串的所有字符变为小写:
str1 = "FishC"
str1.casefold
''fishc''
countsub[, start[, end]]方法之前试过了,就是查找sub子字符串出现的次数,可选参数注: 在Python文档中,用方括号[]括起来表示为可选start和end表示查找的范围:
str1 = "AbcABCabCabcABCabc"
str1.count''ab'', 0, 15
2
如果要查找某个子字符串在该字符串中的位置,可以使用findsub[, start[, end]]或indexsub[, start[, end]]方法。如果找到了,则返回值是第一个字符的索引值; 如果找不到,则find方法会返回-1,而index方法会抛出异常注: 异常是可以被捕获并处理的错误,目前你可以认为就是错误:
str1 = "I love fishc.com"
str1.find"fishc"
7
str1.find"good"
-1
str1.index"fishc"
7
str1.index"good"
Traceback most recent call last:
File "pyshell#12", line 1, in module
str1.index"good"
ValueError: substring not found
今后你可能会在很多文档中看到joinsub的身影,程序员喜欢用它来连接字符串,但它的用法也许会让你感到诧异。join是以字符串作为分隔符,插入到sub字符串中所有的字符之间:
''x''.join"Test"
''Txexsxt''
''_''.join"FishC"
''F_i_s_h_C''
为什么说程序员喜欢用join来连接字符串,我们不是有很好用的连接符号 吗?这是因为当使用连接符号 去拼接大量的字符串时是非常低效率的,因为加号连接会引起内存复制以及垃圾回收操作。所以对于大量的字符串拼接来说,使用join方法的效率要高一些:
''I'' '' '' ''love'' '' '' ''fishc.com''
''I love fishc.com''
'' ''.join[''I'', ''love'', ''fishc.com'']
''I love fishc.com''
replaceold, new[, count]方法如其名,就是替换指定的字符串:
str1 = "I love you"
str1.replace"you", "fishc.com"
''I love fishc.com''
splitsep=None, maxsplit=1跟join正好相反,split用于拆分字符串:
str1 = '' ''.join[''I'', ''love'', ''fishc.com'']
str1
''I love fishc.com''
str1.split
[''I'', ''love'', ''fishc.com'']
str2 = ''_''.join"FishC"
str2.splitsep=''_''
[''F'', ''i'', ''s'', ''h'', ''C'']
5.3.2格式化前面介绍了Python字符串大部分方法的使用,但唯独漏了一个format方法。因为小甲鱼觉得format方法跟本节的话题如出一辙,都是关于字符串的格式化,所以放一块来讲解。
那什么是字符串的格式化,又为什么需要对字符串进行格式化呢?举个小例子给大家听: 某天小甲鱼召开了鱼C国际互联安全大会,到会的朋友有来自世界各地的各界精英人士,有小乌龟、喵星人、旺星人,当然还有米奇和唐老鸭。哇噻,那气势简直跟小甲鱼开了个动物园一样但是问题来了,大家交流起来简直是鸡同鸭讲,不知所云!但是最后聪明的小甲鱼还是把问题给解决了,其实也很简单,各界都找一个翻译就行了,统一都翻译成普通话,那么问题就解决了最后我们这个大会当然取得了卓越的成功并记入了吉尼斯动物大全。举这个例子就是想跟大家说,格式化字符串,就是按照统一的规格去输出一个字符串。如果规格不统一,就很可能造成误会,例如十六进制的10跟十进制的10或二进制的10完全是不同的概念十六进制10等于十进制16,二进制10等于十进制2。字符串格式化,正是帮助我们纠正并规范这类问题而存在的。1. formatformat方法接受位置参数和关键字参数位置参数和关键字参数在函数章节有详细讲解,二者均传递到一个叫作replacement字段。而这个replacement字段在字符串内由大括号{}表示。先看一个例子:
"{0} love {1}.{2}".format"I", "FishC", "com"
''I love FishC.com''
怎么回事呢?仔细看,字符串中的{0}、{1}和{2}应该跟位置有关,依次被format的三个参数替换,那么format的三个参数就叫作位置参数。那什么是关键字参数呢,再来看一个例子:
"{a} love {b}.{c}".formata="I", b="FishC", c="com"
''I love FishC.com''
{a}、{b}和{c}就相当于三个标签,format将参数中等值的字符串替换进去,这就是关键字参数啦。另外,你也可以综合位置参数和关键字参数在一起使用:
"{0} love {b}.{c}".format"I", b="FishC", c="com"
''I love FishC.com''
但要注意的是,如果将位置参数和关键字参数综合在一起使用,那么位置参数必须在关键字参数之前,否则就会出错:
"{a} love {b}.{0}".formata="I", b="FishC", "com"
SyntaxError: non-keyword arg after keyword arg
如果要把大括号打印出来,你有办法吗?没错,这跟字符串转义字符有点像,只需要用多一层大括号包起来即可要打印转义字符\,只需用转义字符转义本身\\:
"{{0}}".format"不打印"
''{0}''
位置参数不打印没有被输出,这是因为{0}的特殊功能被外层的大括号{}剥夺,因此没有字段可以输出。注意,这并不会产生错误哦。最后来看另一个例子:
"{0}: {1:.2f}".format"圆周率", 3.14159
''圆周率: 3.14''
可以看到,位置参数{1}跟平常有些不同,后边多了个冒号。在替换域中,冒号表示格式化符号的开始,.2的意思是四舍五入到保留两位小数点,而f的意思是定点数,所以按照格式化符号的要求打印出了3.14。2. 格式化操作符: %刚才讲的是字符串的格式化方法,现在来谈谈字符串所独享的一个操作符: %,有人说,这不是求余数的操作符吗?是的,没错。当%的左右均为数字的时候,那么它表示求余数的操作; 但当它出现在字符中的时候,它表示的是格式化操作符。表52列举了Python的格式化符号及含义。
表52Python格式化符号及含义
符号含义
%c格式化字符及其ASCII码%s格式化字符串%d格式化整数%o格式化无符号八进制数%x格式化无符号十六进制数%X格式化无符号十六进制数大写%f格式化浮点数字,可指定小数点后的精度%e用科学计数法格式化浮点数%E作用同%e,用科学计数法格式化浮点数%g根据值的大小决定使用%f或%e%G作用同%g,根据值的大小决定使用%f或%E
下面给大家举几个例子参考:
''%c'' % 97
''a''
''%c%c%c%c%c'' % 70, 105, 115, 104, 67
''FishC''
''%d转换为八进制是: %o'' % 123, 123
''123转换为八进制是: 173''
''%f用科学计数法表示为: %e'' % 149500000, 149500000
''149500000.000000用科学计数法表示为: 1.495000e 08''
Python还提供了格式化操作符的辅助指令,如表53所示。
表53格式化操作符的辅助指令
符号含义
m.nm是显示的最小总宽度,n是小数点后的位数结果左对齐 在正数前面显示加号 #在八进制数前面显示''0o'',在十六进制数前面显示''0x64''或''0X64''0显示的数字前面填充''0''代替空格
同样给大家举几个例子供参考:
''%5.1f'' % 27.658
'' 27.7''
''%.2e'' % 27.658
''2.77e 01''
''%10d'' % 5
'' 5''
''%-10d'' % 5
''5 ''
''%010d'' % 5
''0000000005''
''%#X'' % 100
''0X64''
3. Python的转义字符及含义Python的部分转义字符已经使用了一段时间,是时候来给它做个总结了,见表54。
表54转义字符及含义
符号说明符号说明
\''单引号\r回车符\"双引号\f换页符\a发出系统响铃声\o八进制数代表的字符\b退格符\x十六进制数代表的字符\n换行符\0表示一个空字符\t横向制表符TAB\\反斜杠\v纵向制表符
5.4序列聪明的你可能已经发现,小甲鱼把列表、元组和字符串放在一块儿来讲解是有道理的,因为它们之间有很多共同点: 都可以通过索引得到每一个元素。 默认索引值总是从0开始当然灵活的Python还支持负数索引。 可以通过分片的方法得到一个范围内的元素的集合。 有很多共同的操作符重复操作符、拼接操作符、成员关系操作符。我们把它们统称为: 序列!下面介绍一些关于序列的常用BIF内置方法。1. list[iterable]list方法用于把一个可迭代对象转换为列表,很多朋友经常听到迭代这个词,但要是让你解释的时候,很多朋友就含糊其词了: 迭代迭代就是for循环嘛这里小甲鱼帮大家科普一下: 所谓迭代,是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次迭代,而每一次迭代得到的结果会被用来作为下一次迭代的初始值就目前来说,迭代还就是一个for循环,但今后会介绍到迭代器,那个功能,那叫一个惊艳!好了,这里说list方法要么不带参数,要么带一个可迭代对象作为参数,而这个序列天生就是可迭代对象迭代这个概念实际上就是从序列中泛化而来的。还是通过几个例子给大家讲解吧:
# 创建一个空列表
a = list
a
[]
# 将字符串的每个字符迭代存放到列表中
b = list"FishC"
b
[''F'', ''i'', ''s'', ''h'', ''C'']
# 将元组中的每个元素迭代存放到列表中
c = list1, 1, 2, 3, 5, 8, 13
c
[1, 1, 2, 3, 5, 8, 13]
事实上这个list方法大家自己也可以动手实现对不对?很简单嘛,实现过程大概就是新建一个列表,然后循环通过索引迭代参数的每一个元素并加入列表,迭代完毕后返回列表即可。大家课后不妨自己动动手来尝试一下。2. tuple[iterable]tuple方法用于把一个可迭代对象转换为元组,具体的用法和list一样,这里就不啰唆了。3. strobjstr方法用于把obj对象转换为字符串,这个方法在前面结合int和float方法给大家讲过,还记得吧?4. lensublen方法用于返回sub参数的长度:
str1 = "I love fishc.com"
lenstr1
16
list1 = [1, 1, 2, 3, 5, 8, 13]
lenlist1
7
tuple1 = "这", "是", "一", "个", "元组"
lentuple1
5
5. maxmax方法用于返回序列或者参数集合中的最大值,也就是说,max的参数可以是一个序列,返回值是该序列中的最大值; 也可以是多个参数,那么max将返回这些参数中最大的一个:
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
maxlist1
76
str1 = "I love fishc.com"
maxstr1
''v''
max5, 8, 1, 13, 5, 29, 10, 7
29
6. minmin方法跟max用法一样,但效果相反: 返回序列或者参数集合中的最小值。这里需要注意的是,使用max方法和min方法都要保证序列或参数的数据类型统一,否则会出错。
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
list1.append"x"
maxlist1
Traceback most recent call last:
File "pyshell#22", line 1, in module
maxlist1
TypeError: unorderable types: str int
min123, ''oo'', 456, ''xx''
Traceback most recent call last:
File "pyshell#23", line 1, in module
min123, ''oo'', 456, ''xx''
TypeError: unorderable types: str int
人家说: 外行看热闹,内行看门道。分析一下这个错误信息。Python这里说TypeError: unorderable types: str int,意思是说不能拿字符串和整型进行比较。这说明了什么呢?你看,str int,说明max方法和min方法的内部实现事实上类似于之前提到的,通过索引得到每一个元素,然后将各个元素进行对比。所以不妨根据猜想写出可能的代码:
# 猜想下maxtuple1的实现方式
temp = tuple1[0]
for each in tuple1:
if each temp:
temp = each
return temp
由此可见,Python的内置方法其实也没啥了不起的,有些我们也可以自己实现,对吧?所以只要认真跟着本书的内容学习下去,很多看似如狼似虎的问题,将来都能迎刃而解!7. sumiterable[, start]sum方法用于返回序列iterable的总和,用法跟max和min一样。但sum方法有一个可选参数start,如果设置该参数,表示从该值开始加起,默认值是0:
tuple1 = 1, 2, 3, 4, 5
sumtuple1
15
sumtuple1, 10
25
8. sortediterable, key=None, reverse=Falsesorted方法用于返回一个排序的列表,大家还记得列表的内建方法sort吗?它们的实现效果一致,但列表的内建方法sort是实现列表原地排序; 而sorted是返回一个排序后的新列表。
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
list2 = list1[:]
list1.sort
list1
[-98, 0, 1, 13, 18, 32, 34, 54, 76]
sortedlist2
[-98, 0, 1, 13, 18, 32, 34, 54, 76]
list2
[1, 18, 13, 0, -98, 34, 54, 76, 32]
9. reversedsequencereversed方法用于返回逆向迭代序列的值。同样的道理,实现效果跟列表的内建方法reverse一致。区别是列表的内建方法是原地翻转,而reversed是返回一个翻转后的迭代器对象。你没看错,它不是返回一个列表,是返回一个迭代器对象:
list1 = [1, 18, 13, 0, -98, 34, 54, 76, 32]
reversedlist1
list_reverseiterator object at 0x000000000324F518
for each in reversedlist1:
printeach, end='',''
32,76,54,34,-98,0,13,18,1,
10. enumerateiterableenumerate方法生成由二元组二元组就是元素数量为二的元组构成的一个迭代对象,每个二元组是由可迭代参数的索引号及其对应的元素组成的。举个例子你就明白了:
str1 = "FishC"
for each in enumeratestr1:
printeach
0, ''F''
1, ''i''
2, ''s''
3, ''h''
4, ''C''
11. zipiter1 [,iter2 [...]]zip方法用于返回由各个可迭代参数共同组成的元组,举个例子比较容易理解:
list1 = [1, 3, 5, 7, 9]
str1 = "FishC"
for each in ziplist1, str1:
printeach
1, ''F''
3, ''i''
5, ''s''
7, ''h''
9, ''C''
tuple1 = 2, 4, 6, 8, 10
for each in ziplist1, str1, tuple1:
printeach
1, ''F'', 2
3, ''i'', 4
5, ''s'', 6
7, ''h'', 8
9, ''C'', 10
|