Python3内置数据结构
本篇是对Python3内置数据结构的初步学习与小结,用于复习巩固使用,如有理解偏差的地方,还望各位大佬指正。
字符串
字符串定义
-
字符串str是 Python 中最常用的数据类型。
- 一个个字符组成的有序的序列,是字符的集合
- 使用单引号、双引号、三引号包住的字符序列
- 有序序列可索引
- 不可变类型
- str1[2] = ‘s’ 直接报错
- 字符串的各种运算操作,并非是修改字符串,比如字符串的拼接其实就是覆盖命名一个新的字符串。
字符串初始化
字符串初始化后,其本身不可改变。
s1 = 'string' ## 最直接定义
s2 = "str‘i’ng2" ## 用""可以在内部继续使用‘’
s3 = '''this's a
"String"
''' ## ‘’‘ 可以在内部放‘’及””,还可以换行
s4 = 'hello \n magedu.com' ## 转义符\ \n是newline
s5 = 'hello \\n magedu.com' ## \\n 多家一个转义符吧\本身转义
s6 = r"hello \n magedu.com" ## 字符串前写r,取消转义,常用于正则表达式
字符串转义符
- 字符串中存在着一些特殊字符:
- 特殊字符就是在字符串当中起到特殊含义的字符
- “\” 转义符 将字符串转义符后面的紧跟的特殊字符的特殊含义取消掉,或者在行尾作为续行符
符号 | 意义 |
---|---|
\ (在行尾时) | 换行 |
\\ | 反斜杠符号 |
\’ | 单引号 |
\" | 双引号 |
\a | 响铃 |
\b | 退格 |
\e | 转义 |
\000 | 空 |
\n | 换行 |
\v | 纵向制表符 |
\t | 横向制表符 |
\r | 回车 |
\f | 换页 |
字符串元素访问
字符串是有序的字符序列,索引方法与列表相似。
- 字符串支持使用索引访问,与列表相似:
- 索引的用法,取单个元素时,使用字符串[索引值] 索引值为对应元素的索引号;
- 字符串截取:字符串[start:end],得到对应索引范围的元素,该范围是左闭右开的,默认截取的方向是从左往右的,反取:字符串[负数],会变成反向从右往左取;
- 步长截取:字符串[start?step] 按照 step步长截取,step默认为1;
- 若 step > 0, 则表示从左向右切片,start 必须小于 end 才有结果,否则为空。
- 若 step < 0, 也是表示从左到右,但是 start 必须大于 end 才有结果,就变成了反向切片,否则为空。
sql = "select"
sql[4] # 字符串'c'
# sql[4] = 'o' # 字符串是不可变类型,直接报错
sql[0:4]
sql[0:5:2]
sql[::-1] # 省略会按顺序取到最后一位,不会报错也不会少取
c
'sele'
'slc'
'tceles'
- 字符串是有序的字符集合,字符序列:
for c in sql[:5]:
print(type(c), c)
<class 'str'> s
<class 'str'> e
<class 'str'> l
<class 'str'> e
<class 'str'> c
- 字符串是可迭代对象:
经常用list方法将字符串转化成列表。
lst = list(sql)
['s', 'e', 'l', 'e', 'c', 't']
字符串连接
字符串join连接
- 字符串join连接
“string”.join(iterable) -> str - 该join方法常用于列表转字符串,返回一个字符串
- 将可迭代对象连接起来,使用string作为分隔符。
- 可迭代对象本身元素都是字符串。
- 返回一个新字符串。
lst = ['1','2','3']
print("".join(lst)) # 将列表元素无缝组合成一个字符串
print("+++".join(lst)) # 使用666作为分隔符
123
1+++2+++3
字符串 + 连接
- 字符串+连接
str + str -> str- 将2个字符串连接在一起
- 返回一个新字符串
str1 = 's'
print(str1, id(str1))
str1 = str1 + 'r' #
print(str1, id(str1))
s 2781397991640
sr 2781443149248
注意:字符串是不可变类型,没有原对象的修改方式,但是为了方便,允许使用类似重写的方式,用 + 连接字符串,这样使用起来就像是可以修改一样,但是我们查看ID就可以发现已经变量名不是原字符串对象。
字符串分割
- 分割字符串的方法主要有2大类
- split系
- 将字符串按照分隔符分割成若干字符串,并返回列表。
- partition系
- 将字符串按照分隔符分割成2段,返回这2段和分隔符的元组。
- split系
字符串分割split
- split(sep=None, maxsplit=-1) -> list of strings
- 从左向右。
- sep 指定分割字符串,缺省的情况下空白字符串作为分隔符。
- maxsplit 指定分割的最大次数,默认为-1 表示遍历整个字符串,贪婪匹配,尽可能多。
s1 = "I'm \ta super man."
print(s1.split()) # 切空字符串
print(s1.split('s')) # 按分隔符‘s’,贪婪匹配
print(s1.split(' ',maxsplit=2)) # 按分隔符空白分隔符,贪婪匹配
["I'm", 'a', 'super', 'man.']
["I'", ' \ta super ', 'an.']
["I'm", '\ta', 'super man.']
- rsplit(sep=None, maxsplit=-1) -> list of strings
- 从右向左。
- sep 指定分割字符串,缺省的情况下空白字符串作为分隔符。
- maxsplit 指定分割的次数,默认为-1 表示遍历整个字符串,贪婪匹配,尽可能多。
s1 = "I'm \ta super man."
print(s1.rsplit())
print(s1.rsplit('m'))
print(s1.rsplit(' ',maxsplit=2)) # 从右向左切
print(s1.rsplit('m',maxsplit=1)) # 从右向左切
["I'm", 'a', 'super', 'man.']
["I'", ' \ta super ', 'an.']
["I'm \ta", 'super', 'man.']
["I'm \ta super ", 'an.']
- splitlines([keepends]) -> list of strings
- 按照行来切分字符串,返回一个包含各行作为元素的列表。
- keepends 指的是是否保留行分隔符。
- 行分隔符包括\n、\r\n、\r等。
s1 = "I'm\n \ta\r\n super \nman."
print(s1.splitlines())
print(s1.splitlines(True)) # 保留行分隔符
["I'm", ' \ta', ' super ', 'man.']
["I'm\n", ' \ta\r\n', ' super \n', 'man.']
注意:
- 如果字符串中没有split切割符完全对应的子字符串,则返回的列表也就只有一个元素,即字符串本身。
- split切割不保留分隔符。
字符串分割partition
- partition(sep) -> (head, sep, tail)
- 从左至右,遇到分隔符就把字符串分割成两部分,返回头、分隔符、尾三部分的三元组。
- 如果没有找到分隔符,就返回头、2个空元素的三元组。
- sep 分割字符串,必须指定。
s1 = "I'm\n \ta\r\n super \nman."
print(s1.partition('\r')) # 从左往右切成三部分
print(s1.partition('\rnn')) # 没找到分隔符,也返回三元素列表
("I'm", '\n', ' \ta\r\n super \nman.')
("I'm\n \ta\r\n super \nman.", '', '')
- rpartition(sep) -> (head, sep, tail)
- 从右至左,遇到分隔符就把字符串分割成两部分,返回头、分隔符、尾三部分的三元组。
- 如果没有找到分隔符,就返回2个空元素和尾的三元组。
- sep 分割字符串,必须指定。
s1 = "I'm\n \ta\r\n super \nman."
print(s1.rpartition('\n')) # 从右往左切成三部分
print(s1.rpartition('\rnn')) # 没找到分隔符,也返回三元素列表
("I'm\n \ta\r\n super ", '\n', 'man.')
('', '', "I'm\n \ta\r\n super \nman.")
小结:
- partition方法必须指定分隔符,不能缺省。
- partition方法不管有没有找到分隔符,都飞返回3元组列表,即返回头、分隔符、尾三部分的三元组。
字符串修改
replace替换
replace方法用于替换字符串中的每种字符。
- replace(old, new[, count]) -> str
- 从左到右替换指定的元素。
- 字符串中找到匹配替换为新子串,返回新字符串。
- count指定替换次数,不指定就是全部替换。
s1 = "I'm \ta super man."
print(s1.replace('m','MM')) # 替换字符
print(s1.replace('m','MM',1)) # 指定替换次数
I'MM a super MMan.
I'MM a super man.
strip去边
- strip([chars]) -> str
- 从字符串两端去除指定的字符集chars中的所有字符。
- 如果chars没有指定,去除两端的空白字符。
- 返回一个新的字符串。
- lstrip([chars]) -> str
- 从左开始。
- rstrip([chars]) -> str
- 从右开始。
s1 = " 09I'm \ta super man.0 09;"
print(s1.strip()) # 不指定字符集,就去掉空白字符
print(s1.strip(' 0912')) # 从两边最外层开始,去除字符集中有的字符
print(s1.strip(' 9.011;')) # 字符集中的顺序无所谓
print(s1.lstrip(' 9.0;')) # 只去掉左边
print(s1.rstrip(' 9.0;')) # 只去掉右边
09I'm a super man.0 09;
I'm a super man.0 09;
I'm a super man
I'm a super man.0 09;
09I'm a super man
小结:
- strip非常适用于字符串两段杂质的清理,尤其是提供一个无序字符集,使用起来很方便。
- 返回的是一个新的字符串。
字符串查找
find查找
- find(sub[, start[, end]]) -> int
- 在指定的区间[start, end),从左至右,查找子串sub。找到返回索引,没找到返回-1。
- rfind(sub[, start[, end]]) -> int
- 在指定的区间[start, end),从右至左,查找子串sub。找到返回索引,没找到返回-1。
s = "I am very very very sorry"
print(s.find('very'))
print(s.find('very', 5)) # 从字符串的索引为5开始查找,匹配到就立刻返回首位索引值
print(s.find('very', 6, 8)) # 区间查找,没找到返回-1
print(s.rfind('very', 10))
print(s.rfind('very', 10, 15))
print(s.rfind('very', -10,-1)))
5
5
-1
15
10
15
小结:
- find查找子串十分有用,可以方便的获得字符串的索引。
- 子串如果不存在,find会返回-1,而不会直接报错,index则会直接报错。
- 虽然查找看似很方便,但需要遍历字符串,也就是说时间复杂度是O(n)的,如果查找的子串不存在,则必定要遍历整个字符串,使用时需要慎重。
- 有时候我们只想判断这个子串的是否存在,而不想要索引,则可以通过in方法。
sub in str -> bool
得到一个bool型判断值。
index查找
- index(sub[, start[, end]]) -> int
- 在指定的区间[start, end),从左至右,查找子串sub。找到返回索引,没找到抛出异常ValueError。
- rindex(sub[, start[, end]]) -> int
- 在指定的区间[start, end),从左至右,查找子串sub。找到返回索引,没找到抛出异常ValueError。
s = "I am very very very sorry"
s.index('very')
s.index('very', 5)
s.rindex('very', 10) # 从指定的区间开始,从右往左查找
s.rindex('very', 10, 15) # 从指定的区间开始,从右往左查找
s.rindex('very',-10,-1)
s.index('very', 6, 8) # 区间内子串不存在,直接报错
5
5
15
10
15
小结:
- index查找与find查找基本一致,最大区别在于子串不存在时index会直接报错,因此除非业务需要,我们一般选用find查找。
count计数
- count(sub[, start[, end]]) -> int
- 在指定的区间[start, end),从左至右,统计子串sub出现的次数
s = "I am very very very sorry"
print(s.count('very')) # 不指定子串区间,从左到右计数
print(s.count('very', 5)) # 指定子串区间,从左到右计数
print(s.count('very', 10, 14)) # 指定子串区间,从左到右计数
3
3
1
小结:
- count计数对于很多业务而言的是必要的方法。
- find, index, count 涉及遍历,时间复杂度为O(n)。随着列表数据规模的增大,而效率下降,在嵌套循环中使用性能下降尤为明显。
- 有时可以利用已有的遍历实现计数,避免嵌套循环使用count方法。
字符串判断
字符串首尾判断
- startswith(prefix[, start[, end]]) -> bool
- 在指定的区间[start, end),字符串是否是prefix开头
- endswith(suffix[, start[, end]]) -> bool
- 在指定的区间[start, end),字符串是否是suffix结尾
s = "I am very very very happy"
print(s.startswith('am')) # 从头开始,连续匹配子串
print(s.startswith('am', 2))
print(s.startswith('very', 5, 9))
print(s.endswith('very', 5, 9)) # 从尾开始,连续匹配子串
print(s.endswith('happy', 5))
print(s.endswith('happy', 5, -1))
print(s.endswith('happy', 5, 100)) # 区间超界也可以
False
True
True
True
True
False
True
小结:
- 首尾判断,对于处理一些格式的字符内容,非常方便,配合strip使用效果更佳。
- 由于只判断首尾,效率很高,推荐使用。
字符串is判断
判断方法 | 判断内容 |
---|---|
isalnum | 判断字符串是否完全由字母戒数字组成 |
isalpha | 判断字符串是否完全由字母组成 |
isdigit | 判断字符串是否完全由数字组成 |
isupper | 判断字符串当中的字母是否完全是大写 |
islower | 判断字符串当中的字母是否完全是小写 |
istitle | 判断字符串是否满足 title 格式 |
isspace | 判断字符串是否完全由空格组成 |
'12345a'.isalnum()
'adcdef'.isalpha()
'123456'.isdigit()
'HellO'.isupper()
True
True
True
False
小结:
- 使用is方法判断字符串是否符合内容要求,是业务常用方法,很多时候,我们需要判断输入的字符串是什么类型,是否符合要求。
字符串排版
方法 | 字符串内容格式化修改 |
---|---|
upper | 将字符串当中所有的字母转换为大写 |
lower | 将字符串当中所有的字母转换为小写 |
swapcase | 将字符串当中所有的字母大小写互换 |
title | 标题化,将单词首字母大写,单词以非字母划分 |
capitalize | 只有字符串的首字母大写 |
expandtabs | 把字符串中的制表符号(’\t’)转为空格,(’\t’)默认的空格数是 8 |
'hello'.upper()
'Hello'.swapcase()
'hello world'.title()
'for\tis\tcool'.expandtabs(4)
'for\tis\tcool'.expandtabs() # ‘\t’ 变成空白符,默认8个
'HELLO'
'hELLO'
'Hello World'
'for is cool'
'for is cool'
字符串格式化
- 字符串的格式化是一种拼接字符串输出样式的手段,更灵活方便
- join拼接只能使用分隔符,且要求被拼接的是可迭代对象且其元素是字符串。
- + 拼接字符串还算方便,但是非字符串需要先转换为字符串才能拼接。
printf-style formatting
- 在2.5版本之前,只能使用printf style风格的print输出
- printf-style formatting,来自于C语言的printf函数。
- 格式要求
- 占位符:使用%和格式字符组成,例如%s、%d等。
- s调用str(),r会调用repr()。所有对象都可以被这两个转换。
- 占位符中还可以插入修饰字符,例如%03d表示打印3个位置,不够前面补零。
- format % values,格式字符串和被格式的值之间使用%分隔。
- values只能是一个对象,或是一个与格式字符串占位符数目相等的元组,或一个字典。
- 占位符:使用%和格式字符组成,例如%s、%d等。
"I am %03d" % (20,)
'I like %s.' % 'Python'
'%3.2f%% , 0x%x, 0X%02X' % (89.7654, 10, 15)
"I am %-5d" % (20,)
'I am 020'
'I like Python.'
'89.77% , 0xa, 0X0F'
'I am 20 '
小结
- printf-style formatting 是C语言风格的字符串格式化,大部分C系列的语言都支持这种格式化风格。
- Python有自己的字符串格式化方法,format函数更优雅,功能更强大,推荐使用。
format函数-- 推荐使用
-
format函数格式字符串语法——Python风格推荐使用
- ="{} {xxx}".format(*args, **kwargs) -> str
- args是可变位置参数,是一个元组,用于位置赋值。
- kwargs是可变关键字参数,是一个字典,用于关键字赋值。
- 花括号表示占位符。
- {}表示按照顺序匹配位置参数,{n}表示取位置参数索引为n的值。
- {xxx}表示在关键字参数中搜索名称一致的。
- {{}} 表示打印花括号,如果有多余的位置参数,会送到里面的{}中嵌套传参。
-
位置参数
- 按照位置顺序用位置参数替换前面的格式字符串的占位符中。
"{}:{}".format('192.168.1.100',8888)
'192.168.1.100:8888'
-
关键字参数或命名参数
- 位置参数按照序号匹配,关键字参数按照名词匹配。
"{server} {1}:{0}".format(8888, '192.168.1.100', server='Web Server Info : ')
'Web Server Info : 192.168.1.100:8888'
-
访问元素
- 通过索引访问参数元素。
"{0[0]}.{0[1]}".format(('xxx','com'))
'xxx.com'
- 这种情况一般可以直接参数解构,转变成位置参数。
"{}.{}".format(*('xxx','com'))
'xxx.com'
-
对象属性访问
- 通过format函数传入对象,并调用该对象的方法 。
from collections import namedtuple Point = namedtuple('Point','x y') p = Point(4,5) print("{{{0.x},{0.y}}}".format(p))
{4,5}
-
格式化对齐
- 字符串面向人类的数据类型,这时候打印的格式化问题就很重要。format提供了强大的字符格式化方法,包括居中,居左,居右,填充,占位等。
'{0}*{1}={2:<2}'.format(3,2,2*3) # 居左打印位置参数2
'{0}*{1}={2:>04}'.format(3,2,2*3) # 居右打印位置参数2,填充0
'{:^30}'.format('centered') # 居中打印
'{:*^30}'.format('centered') # 居中打印,填充*
'3*2=6 '
'3*2=0006'
' center '
'************center************'
- 进制打印
- 利用format函数,将参数格式化为X进制显示。
"int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42) # 不打印进制标志符
"int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42) # 不打印进制标志符
octets = [192, 168, 0, 1]
'{:#02X}|{:02X}|{:02X}|{:02X}'.format(*octets) # 参数解构填充,并按大写十六进制打印
'int: 42; hex: 2a; oct: 52; bin: 101010'
'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010'
'0XC0|A8|00|01'
- 浮点数
- 与C风格相似,Python风格的format函数也可以对浮点型数据进行精细的格式化打印。
print("{}".format(3**0.5)) # 1.7320508075688772
print("{:f}".format(3**0.5)) # 1.732051,精度默认6
print("{:10f}".format(3**0.5)) # 右对齐,宽度10
print("{:2}".format(102.231)) # 宽度为2
print("{:.2}".format(3**0.5)) # 1.7 2个数字
print("{:.2f}".format(3**0.5)) # 1.73 小数点后2位
print("{:3.2f}".format(3**0.5)) # 1.73 宽度为3,小数点后2位
print("{:3.3f}".format(0.2745)) # 0.275
print("{:3.3%}".format(1/3)) # 33.333%
1.7320508075688772
1.732051
1.732051
102.231
1.7
1.73
1.73
0.275
33.333%
format格式化经典–九九乘法表
- 打印九九乘法表
- 九九乘法表是个练习循环与格式化打印的经典案例
for i in range(1, 10):
for j in range(1, i + 1): # 控制循环
sub = i * j
space = ' ' if j > 1 and sub < 10 else ' ' #控制个数数间距
print('{}*{}={}'.format(j, i, sub), end= space) #格式化打印
print('') # 每次循环结束补一个回车
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
总结:
- 对于同一业务,可以有多重格式化方法,重点在于是否可以灵活的使用格式化函数,而这就需要在学习使用中不断的积累。