字符串和常用数据结构 使用字符串 第二次世界大战促使了现代电子计算机的诞生,最初的目的用计算机来快速的完成导弹弹道的计算,因此在计算机刚刚诞生的那个年代,计算机处理的信息基本上都是数值型的信息,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机更多的时间需要处理的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
所谓字符串 ,就是由零个或多个字符组成的有限序列,一般记为 。
我们可以通过下面的代码来了解字符串的使用。
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 def main () : str1 = 'hello, world!' print(len(str1)) print(str1.capitalize()) print(str1.upper()) print(str1.find('or' )) print(str1.find('shit' )) print(str1.startswith('He' )) print(str1.startswith('hel' )) print(str1.endswith('!' )) print(str1.center(50 , '*' )) print(str1.rjust(50 , ' ' )) str2 = 'abc123456' print(str2[2 ]) print(str2[2 :5 ]) print(str2[2 :]) print(str2[2 ::2 ]) print(str2[::2 ]) print(str2[::-1 ]) print(str2[-3 :-1 ]) print(str2.isdigit()) print(str2.isalpha()) print(str2.isalnum()) str3 = ' jackfrued@126.com ' print(str3) print(str3.strip()) if __name__ == '__main__' : main()
除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。
使用列表 下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
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 def main () : list1 = [1 , 3 , 5 , 7 , 100 ] print(list1) list2 = ['hello' ] * 5 print(list2) print(len(list1)) print(list1[0 ]) print(list1[4 ]) print(list1[-1 ]) print(list1[-3 ]) list1[2 ] = 300 print(list1) list1.append(200 ) list1.insert(1 , 400 ) list1 += [1000 , 2000 ] print(list1) print(len(list1)) list1.remove(3 ) if 1234 in list1: list1.remove(1234 ) del list1[0 ] print(list1) list1.clear() print(list1) if __name__ == '__main__' : main()
和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def main () : fruits = ['grape' , 'apple' , 'strawberry' , 'waxberry' ] fruits += ['pitaya' , 'pear' , 'mango' ] for fruit in fruits: print(fruit.title(), end=' ' ) print() fruits2 = fruits[1 :4 ] print(fruits2) fruits3 = fruits[:] print(fruits3) fruits4 = fruits[-3 :-1 ] print(fruits4) fruits5 = fruits[::-1 ] print(fruits5) if __name__ == '__main__' : main()
下面的代码实现了对列表的排序操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def main () : list1 = ['orange' , 'apple' , 'zoo' , 'internationalization' , 'blueberry' ] list2 = sorted(list1) list3 = sorted(list1, reverse=True ) list4 = sorted(list1, key=len) print(list1) print(list2) print(list3) print(list4) list1.sort(reverse=True ) print(list1) if __name__ == '__main__' : main()
我们还可以使用列表的生成式语法来创建列表,代码如下所示。
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 sysdef main () : f = [x for x in range(1 , 10 )] print(f) f = [x + y for x in 'ABCDE' for y in '1234567' ] print(f) f = [x ** 2 for x in range(1 , 1000 )] print(sys.getsizeof(f)) print(f) f = (x ** 2 for x in range(1 , 1000 )) print(sys.getsizeof(f)) print(f) for val in f: print(val) if __name__ == '__main__' : main()
除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过yield
关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成斐波拉切数列 的生成器。所谓斐波拉切数列可以通过下面递归 的方法来进行定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def fib (n) : a, b = 0 , 1 for _ in range(n): a, b = b, a + b yield a def main () : for val in fib(20 ): print(val) if __name__ == '__main__' : main()
使用元组 Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
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 def main () : t = ('骆昊' , 38 , True , '四川成都' ) print(t) print(t[0 ]) print(t[3 ]) for member in t: print(member) t = ('王大锤' , 20 , True , '云南昆明' ) print(t) person = list(t) print(person) person[0 ] = '李小龙' person[1 ] = 25 print(person) fruits_list = ['apple' , 'banana' , 'orange' ] fruits_tuple = tuple(fruits_list) print(fruits_tuple) if __name__ == '__main__' : main()
这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
元组中的元素是无法修改的,事实上我们在项目中尤其是多线程 环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
元组在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间,下图是我的macOS系统上测试的结果。
使用集合 Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。
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 def main () : set1 = {1 , 2 , 3 , 3 , 3 , 2 } print(set1) print('Length =' , len(set1)) set2 = set(range(1 , 10 )) print(set2) set1.add(4 ) set1.add(5 ) set2.update([11 , 12 ]) print(set1) print(set2) set2.discard(5 ) if 4 in set2: set2.remove(4 ) print(set2) for elem in set2: print(elem ** 2 , end=' ' ) print() set3 = set((1 , 2 , 3 , 3 , 2 , 1 )) print(set3.pop()) print(set3) print(set1 & set2) print(set1 | set2) print(set1 - set2) print(set1 ^ set2) print(set2 <= set1) print(set3 <= set1) print(set1 >= set2) print(set1 >= set3) if __name__ == '__main__' : main()
说明: Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如&
运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。
使用字典 字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
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 def main () : scores = {'骆昊' : 95 , '白元芳' : 78 , '狄仁杰' : 82 } print(scores['骆昊' ]) print(scores['狄仁杰' ]) for elem in scores: print('%s\t--->\t%d' % (elem, scores[elem])) scores['白元芳' ] = 65 scores['诸葛王朗' ] = 71 scores.update(冷面=67 , 方启鹤=85 ) print(scores) if '武则天' in scores: print(scores['武则天' ]) print(scores.get('武则天' )) print(scores.get('武则天' , 60 )) print(scores.popitem()) print(scores.popitem()) print(scores.pop('骆昊' , 100 )) scores.clear() print(scores) if __name__ == '__main__' : main()
练习 练习1:在屏幕上显示跑马灯文字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import osimport timedef main () : content = '北京欢迎你为你开天辟地…………' while True : os.system('cls' ) print(content) time.sleep(0.2 ) content = content[1 :] + content[0 ] if __name__ == '__main__' : main()
练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import randomdef generate_code (code_len=4 ) : """ 生成指定长度的验证码 :param code_len: 验证码的长度(默认4个字符) :return: 由大小写英文字母和数字构成的随机验证码 """ all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' last_pos = len(all_chars) - 1 code = '' for _ in range(code_len): index = random.randint(0 , last_pos) code += all_chars[index] return code
练习3:设计一个函数返回给定文件名的后缀名。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def get_suffix (filename, has_dot=False) : """ 获取文件名的后缀名 :param filename: 文件名 :param has_dot: 返回的后缀名是否需要带点 :return: 文件的后缀名 """ pos = filename.rfind('.' ) if 0 < pos < len(filename) - 1 : index = pos if has_dot else pos + 1 return filename[index:] else : return ''
练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。 1 2 3 4 5 6 7 8 9 def max2 (x) : m1, m2 = (x[0 ], x[1 ]) if x[0 ] > x[1 ] else (x[1 ], x[0 ]) for index in range(2 , len(x)): if x[index] > m1: m2 = m1 m1 = x[index] elif x[index] > m2: m2 = x[index] return m1, m2
练习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 29 30 31 32 33 34 35 36 37 38 def is_leap_year (year) : """ 判断指定的年份是不是闰年 :param year: 年份 :return: 闰年返回True平年返回False """ return year % 4 == 0 and year % 100 != 0 or year % 400 == 0 def which_day (year, month, date) : """ 计算传入的日期是这一年的第几天 :param year: 年 :param month: 月 :param date: 日 :return: 第几天 """ days_of_month = [ [31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ], [31 , 29 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ] ][is_leap_year(year)] total = 0 for index in range(month - 1 ): total += days_of_month[index] return total + date def main () : print(which_day(1980 , 11 , 28 )) print(which_day(1981 , 12 , 31 )) print(which_day(2018 , 1 , 1 )) print(which_day(2016 , 3 , 1 )) if __name__ == '__main__' : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def main () : num = int(input('Number of rows: ' )) yh = [[]] * num for row in range(len(yh)): yh[row] = [None ] * (row + 1 ) for col in range(len(yh[row])): if col == 0 or col == row: yh[row][col] = 1 else : yh[row][col] = yh[row - 1 ][col] + yh[row - 1 ][col - 1 ] print(yh[row][col], end='\t' ) print() if __name__ == '__main__' : main()
综合案例 案例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 26 27 28 29 30 31 32 33 34 from random import randrange, randint, sampledef display (balls) : """ 输出列表中的双色球号码 """ for index, ball in enumerate(balls): if index == len(balls) - 1 : print('|' , end=' ' ) print('%02d' % ball, end=' ' ) print() def random_select () : """ 随机选择一组号码 """ red_balls = [x for x in range(1 , 34 )] selected_balls = [] selected_balls = sample(red_balls, 6 ) selected_balls.sort() selected_balls.append(randint(1 , 16 )) return selected_balls def main () : n = int(input('机选几注: ' )) for _ in range(n): display(random_select()) if __name__ == '__main__' : main()
说明: 上面使用random模块的sample函数来实现从列表中选择不重复的n个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 """ 《幸运的基督徒》 有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。 """ def main () : persons = [True ] * 30 counter, index, number = 0 , 0 , 0 while counter < 15 : if persons[index]: number += 1 if number == 9 : persons[index] = False counter += 1 number = 0 index += 1 index %= 30 for person in persons: print('基' if person else '非' , end='' ) if __name__ == '__main__' : main()
综合案例3:井字棋 游戏 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 import osdef print_board (board) : print(board['TL' ] + '|' + board['TM' ] + '|' + board['TR' ]) print('-+-+-' ) print(board['ML' ] + '|' + board['MM' ] + '|' + board['MR' ]) print('-+-+-' ) print(board['BL' ] + '|' + board['BM' ] + '|' + board['BR' ]) def main () : init_board = { 'TL' : ' ' , 'TM' : ' ' , 'TR' : ' ' , 'ML' : ' ' , 'MM' : ' ' , 'MR' : ' ' , 'BL' : ' ' , 'BM' : ' ' , 'BR' : ' ' } begin = True while begin: curr_board = init_board.copy() begin = False turn = 'x' counter = 0 os.system('clear' ) print_board(curr_board) while counter < 9 : move = input('轮到%s走棋, 请输入位置: ' % turn) if curr_board[move] == ' ' : counter += 1 curr_board[move] = turn if turn == 'x' : turn = 'o' else : turn = 'x' os.system('clear' ) print_board(curr_board) choice = input('再玩一局?(yes|no)' ) begin = choice == 'yes' if __name__ == '__main__' : main()
说明: 最后这个案例来自《Python编程快速上手:让繁琐工作自动化》 一书(这本书对有编程基础想迅速使用Python将日常工作自动化的人来说还是不错的选择),对代码做了一点点的调整。