序列
序列是一种有序的集合,就是诸如数组、链表或者对外呈现类似结构的数据结构,特点就是拥有一串的元素,并且这些元素的存放是有顺序的。
python内置多种序列类型,对所有这些序列类型,有一些通用的操作方法可以调用(见后面'序列的一般操作'章节),同时各种序列类型各自还有一些特有方法。
字符串
字符串str是一种字符序列,每个元素是一个字符,python没有单个字符类型,只有字符串类型,一个字符也是字符串。
python对字符串的支持非常强大,使用起来很方便。
字符串字面量
字符串字面量(literal)就是字符串常量的书写方式,包括单引号、双引号、三引号,raw字符多种方式
-
单引号
用单引号把一串字符包起来,就构成一个字符串,特殊字符需要用转义,转义方法和c语言基本类似。>>> string = 'life is short, you need python' >>> print(string) #print是内置打印函数,类似c语言的printf,注意print函数默认会自动添加换行 life is short, you need python >>> >>> string = 'life is short, \nyou need python\n' >>> print(string) life is short, you need python >>> >>> len(string) #内置函数len可以获取字符串长度即字符个数,注意不像c语言的字符串,python字符串是不需要'\0'结尾的 32
-
双引号
双引号和单引号的用法类似,但双引号里可以包含单引号,单引号里可以包含双引号而不需要转义。>>> string = "life is short, 'you need python'" >>> print(string) life is short, 'you need python' >>> >>> string = 'life is short, "you need python"' >>> print(string) life is short, "you need python" >>>
-
三引号
三引号支持多行, 换行、tab等特殊字符可以直接输入,所见即所得。三引号字符串也同样可以使用转义字符。>>> string = '''life is short, ... \tyou need python''' # 在python命令中换行时,会显示‘...’, 表示可以继续输入 >>> print(string) life is short, you need python >>>
-
raw字符串
raw字符串用于禁止转义, 用小写r和和大写R作前缀,可以和前面的几种引号组合>>> string = r'life is short, \nyou need python\n' >>> print(string) life is short, \nyou need python\n >>>
格式化字符串
字符串的格式化不需要调用函数,直接使用 % 实现
>>> string = 'string = %s' % 'abc'
>>> print(string)
string = abc
>>>
>>> string = '%s = %d' % ('abc', 123) #多个参数要使用括号
>>> print(string)
abc = 123
字符串编码
字符在内存中是用16位unicode表示的,因此字符可以不只是ascii字符,还可以包括中文、日文、德文等世界上几乎所有的语言字符。可以用内置函数 ord 获取单个字符的unicode值,用内置函数 chr 把单个unicode值转成单个字符,还可以用16进制unicode值直接书写字符串。
>>> ord('a')
97
>>> chr(97)
'a'
>>>
>>> ord('我')
25105
>>> chr(25105)
'我'
>>>
>>> string = '\u6211\u662f\u8c01'
>>> print(string)
我是谁
>>>
字符串在python内存中的编码方式是unicode,每个字符固定2字节编码,处理速度快但浪费存储空间。一般各种文档为了节省存储空间,使用的编码不是直接用unicode,而是用诸如ASCII(纯英文)、国标GB2312(汉字)、UTF-8(通用)等其他编码,这些编码对不同字符采用不同长度的编码,可节省存储空间。
python解析器解析代码时,默认是以UTF-8编码来读取的,因此若非有特殊原因,python代码文件建议用UTF-8编码保存。
字符串通过 encode 方法把unicode编码转成其他编码方式,生成字节串,生成的字节串可以用于传输或写到文件中。
注意:原始的字符串并没有被改变,返回的字节串是新生成的
>>> 'who am i'.encode(encoding="utf-8")
b'who am i'
>>>
>>> 'who am i'.encode(encoding="ascii")
b'who am i'
>>>
>>> 'who am i, 我是谁'.encode(encoding="utf-8")
b'who am i, \xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81'
>>>
>>> 'who am i, 我是谁'.encode(encoding="gb2312")
b'who am i, \xce\xd2\xca\xc7\xcb\xad'
>>>
编码后的字节串可以通过 decode 方法解出字符串,还可以通过内置函数 str 解出字符串
注意:原始的字节串并没有被改变,返回的字符串是新生成的
>>> b'who am i'.decode(encoding="utf-8")
'who am i'
>>>
>>> b'who am i'.decode(encoding="ascii")
'who am i'
>>> b'who am i, \xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81'.decode(encoding="utf-8")
'who am i, 我是谁'
>>>
>>> b'who am i, \xce\xd2\xca\xc7\xcb\xad'.decode(encoding="gb2312")
'who am i, 我是谁'
>>>
>>> str(b'who am i, \xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81', encoding="utf-8")
'who am i, 我是谁'
>>>
字符串方法
字符串内置很多方法(字符串其实是对象,python里一切皆对象),以下罗列出常用的方法并简要说明,需要用到时可以再查阅python文档。
-
统计
方法 说明 str.count(sub[, start[, end]]) 统计子字符串 -
编码
方法 说明 str.encode(encoding="utf-8", errors="strict") 编码,转成字节串bytes -
查找和替换
方法 说明 str.startswith(prefix[, start[, end]]) 检查字符串前缀 str.endswith(suffix[, start[, end]]) 检查字符串后缀 str.find(sub[, start[, end]]) 正序(即左查找)查找字符串,返回第一个找到的位置,没找到则返回-1 str.rfind(sub[, start[, end]]) 右查找 str.index(sub[, start[, end]]) 和find的唯一区别是,没找到抛出ValueError异常 str.rindex(sub[, start[, end]]) 和rfind的唯一区别是,没找到抛出ValueError异常 str.replace(old, new[, count]) 替换字符串 str.expandtabs(tabsize=8) 把tabs转成空格,注意并不是每个tab都转成tabsize个空格,而是跟列对齐有关 -
格式化
方法 说明 str.format(*args, **kwargs) 格式化 str.format_map(mapping) 格式化,参数是个字典 -
检查字符串
方法 说明 str.isalnum() 是否由字母和数字组成 str.isalpha() 是否只由字母组成 str.isdecimal() 是否是10进制数 str.isdigit() 是否只包含‘0’~‘9’ str.isidentifier() 是否是有效的Python标识符 str.islower() 是否全是小写 str.isnumeric() 是否是数字,可以不只包括数字字符‘0’~‘9’, 甚至可以包括汉字‘一’、‘二’、‘三’等 str.isprintable() 是否全是可打印字符 str.isspace() 是否全是空白字符 str.istitle() 是否是标题,标题的每个单词的首字母大写 str.isupper() 是否全是大写 -
对齐处理
方法 说明 str.center(width[, fillchar]) 居中处理 str.ljust(width[, fillchar]) 左对齐, 并使用空格填充至长度width str.rjust(width[, fillchar]) 右对齐, 并使用空格填充至长度width str.zfill(width) 右对齐, 并使用‘0’填充至长度width -
大小写处理
方法 说明 str.capitalize() 把首字母变成大写,其余变成小写 str.casefold() 变成小写,不仅仅针对英文字母,也包括其他字母,如德文 str.lower() 转为全小写 str.upper() 转为全大写 str.swapcase() 翻转大小写 str.title() 标题化,每个单词首字母大写 -
去头去尾处理
方法 说明 str.lstrip([chars]) 去头 str.rstrip([chars]) 去尾 str.strip([chars]) 去头去尾 -
映射处理
方法 说明 static str.maketrans(x[, y[, z]]) 创建字符映射表, 是个静态方法 str.translate(table) 根据映射表,转换字符串 -
连接和分割处理
方法 说明 str.join(iterable) 用字符串作为分隔符,把序列中的元素连接起来 str.partition(sep) 正序分离字符串,sep为第一个 str.rpartition(sep) 反序分离字符串,sep为最后一个 str.split(sep=None, maxsplit=-1) 正序分割字符串,可以指定最多分割次数,从左到右查找sep str.rsplit(sep=None, maxsplit=-1) 反序分割字符串,可以指定最多分割次数 str.splitlines([keepends]) 按行分割
二进制字节串
二进制字节串有两种类型,bytes和bytearray,bytes是不可变的,一旦初始化就不可修改,对其修改都是生成新字节串,bytearray的元素可以修改,并且长度也是可变的。
-
bytes字面量
bytes常量用 b 前缀加上ascii字符串或小于256的16进制或8进制数表示>>> a = b'\x10\x11\x12' #每个元素用2位16进制数表示 >>> >>> a = b'\20\21\022\347' #每个元素用2或3位8进制数表示, 个人建议不要用8进制,比如b'\378', 最后的8不是8进制数的一部分,晕吧? >>> >>> a = b'abc' #每个元素用1个asciii字符表示 >>> >>> a = b'\x10\x11\x12abc' #混合使用16进制数和ascii字符 >>> >>> len(a) #获取字节串长度 6
-
字节串创建
>>> a = bytes() >>> a b'' #不是None >>> >>> a = bytearray() >>> a bytearray(b'') #不是None >>> >>> a = bytes(10) >>> a b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #元素默认为0 >>> >>> a = bytearray(10) >>> a bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') ##元素默认为0 >>> >>> a = bytes(range(10)) >>> a b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t' >>>
实际上,bytes或bytearray可以通过bytes(obj)或bytearray(obj)创建,只要obj支持buffer protocol。至于什么是buffer protocol可以查阅python规范
-
字节串方法
str的方法几乎都有对应的bytes和bytearray方法,这里只列出几个特殊的。>>> bytes.fromhex('2Ef0F1f2') # b'.\xf0\xf1\xf2' >>> >>> b'.\xf0\xf1\xf2'.hex() '2ef0f1f2' >>>
列表和元组
列表和元组的每个元素可以是不同的数据类型,元组是不可变的,一旦初始化就不可更改,列表是可变的。
-
创建
列表用中括号或内置函数list创建,元组用圆括号或内置函数tuple创建。>>> a = [] # 空列表 >>> a [] >>> b = () # 空元组 >>> b () >>> >>> a = (1,2,'aaa', None) # 初始化值 >>> a (1, 2, 'aaa', None) >>> >>> b = [1,2,'aaa', None] # 初始化值 >>> b [1, 2, 'aaa', None] >>> >>> a = [1, 2, [3, 4], (5, 6)] # 元素甚至可以是列表或元组 >>> a [1, 2, [3, 4], (5, 6)]
-
列表推导
即List Comprehensions,是指创建列表时,列表的每一个元素通过一个表达式来推导,表达式一般是个for循环, 可以带if表达式,还可以是多重循环。
使用列表推导可以精简代码,减少代码行,但不建议写太复杂的推导表达式,可读性太差。
注意元组没有类似推导,如果写成'元组推导',实际生成的不是元组,而是另一种东西,叫生成器(generator),后面会讲。[x*x for x in range(1, 11)] # 列表为:[1x1, 2x2, 3x3, ..., 10x10] [x*x for x in range(1, 11) if x % 2 == 0] # 列表为:[2x2, 4x4, ..., 10x10], 只有偶数部分 a = (1, 2, 3) b = (4, 5, 6) [x*y for x in a for y in b] # 列表为:[4, 5, 6, 8, 10, 12, 12, 15, 18] (x*x for x in range(1, 11)) # 生成的不是元组,是生成器
列表排序
列表内建排序 sort(, key=None, reverse=None)* 方法,使用 '<' 比较元素,排序是在原列表上直接操作的,如果某个元素比较失败,则整个排序失败,并且已经被修改的元素不会还原。
参数 key:一个函数,用来预处理每个比较 key, 比如 key=str.lower
参数 reverse:是否反序,True则反序。
元组没有排序方法,因为元组是不可变的。
range
range用来生成一个数序列, 一般用在 for 循环中,range是不可变的,一旦生成后就不可更改,range的每个元素是在访问到时才生成的,因此就算是一个很大整数的range也不会占用太多内存。这点和元组不同,也是为什么有元组了为何还需要range类型的原因。
>>> range(10)
range(0, 10)
>>> a = range(10)
>>> a
range(0, 10)
>>>
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> list(range(0, 10, 2))
[0, 2, 4, 6, 8]
序列的一般操作
字符串、字节串、元组、列表等序列类型有一些通用的操作。
-
通用操作
操作 说明 x in s 检查序列中是否有元素等于x,有为True,否则为False x not in s 以上面刚好相反 s + t 连接两个序列, 注意:如果是不可变序列,则返回新的序列,不是修改原序列 s[i] 第i个元素, i可以为负数,-1表示最后一个,-2为倒数第二个,以此类推 s[i:j] 子序列切片:第i到第j-1元素, i,j可以为负数,-1表示最后一个,-2为倒数第二个,以此类推 len(s) 序列长度 s * n or n * s 相当于序列重复n次 s[i] 第i个元素 s[i:j] 子序列切片:第i到第j-1元素 s[i:j:k] 子序列切片:第i到第j-1元素,间隔为k min(s) 元素最小值 max(s) 元素最大值 s.index(x[, i[, j]]) 从[i:j]中第一个值等于x的元素的索引 s.count(x) 值等于x的元素个数 -
可变序列特有的操作
可变序列是指序列的元素在是可以修改的,列表是可变序列,字符串、字节串、元组、range是不可变序列操作 说明 s[i] = x 第i个元素改写为x s[i:j] = t 用t[0:j-i]替换s[i:j],t不一定是序列,只要是可迭代对象就行 del s[i:j] 删除第i到第j-1元素,也可以写成 s[i:j] = [] s[i:j:k] = t 跟s[i:j] = t类似,指定间隔 del s[i:j:k] 跟del s[i:j]类似,指定间隔 s.append(x) 把x加入到序列的尾部 s.clear() 清空序列s 也可以写成 del s[:] s.copy() 浅拷贝 也可以写成 s[:] s.extend(t) or s += t 把序列t加入到序列s的尾部 s *= n 相当于序列s重复n次,直接修改原序列s s.insert(i, x) 把x插入到i位置 也可以写成 s[i:i] = [x] s.pop([i]) 弹出i元素,即返回i元素,并从序列中删除i元素 s.remove(x) 删除序列中等于x的元素,如果有多个,则只删除第一个 s.reverse() 第一个元素和最后一个对调,第二个和倒数第二个对调,以此类推