Python序列:字符串、数组、元组

作者:最西瓜 来源:《Core Python Programming》CH6. Sequences: Strings, Lists, and tuples

通用操作符

Python中的序列支持一些通用的操作符可以作用于字符串、列表和与元组。这些操作符包括:

  • 成员关系(in,not in):用于判断一个元素是否在序列中,如一个字符串是否在另一个字符串中,元素是否在列表或者元组中;
  • 连接符(+)用于将两个类型相同的序列连接起来,注意这会生成一个新的序列而不改变原有的序列。并且这并不是最有效的方式,对于字符串推荐用 join() 方法,对于List推荐用 extend() 方法;
  • 重复(*)用于将序列重复多次并返回一个新的序列,用法如下:sequence * copies_int;
  • 切片([],[ : ],[ : : ])[index] 将访问一个元素,[start_index:end_index] 将访问一段元素,end_index 是不包含的,[start_index:end_index:stride] 以步进的方式访问列表中的元素;

切片是Python序列非常重要的特性,值得多说几句。首先单值索引不能超出范围,否则会抛出IndexError异常。其次切片索引允许负值,范围是 -len(sequence) <= index <= -1 表示从尾部开始计算,-1表示最末尾的元素,-len(sequence) 表示第一个元素。

sequence[start_index:end_index]中 start_index 和 end_index 都是可选的,如果不提供就分别表示从头开始或者到末尾结束。并且索引是可以超出范围的,超出部分就计算到两个端点。使用None作为索引和不同索引的结果是一样的。

1
2
3
s = 'abcde'
for i in [None] + range(-1, -len(s), -1):
    print s[:i]

内建函数

Python为序列提供不少的内建函数,除了构建函数还是一些操作函数,所有接受 iter 参数的函数可以接受迭代器而不仅仅是序列,参数为 seq 的就只能接受序列。所谓迭代器是一种比序列更通用的概念,表示能够依次访问得到顺序值。

  • list(iter) 和 tuple(iter) 很多时候用来进行相互转换;
  • str() 常用来获取别的对象的字符串表示,也常用来转换数字;
  • len(seq) 获取序列的长度;
  • enumerate(iter) 返回迭代的枚举对象,每次访问枚举对象都会返回一个(index,item)的元组;
  • max(iter, key=None) 或 max(arg0, arg1…, key=None) 返回迭代器或者参数列表的最大值;
  • min(iter, key=None) 或 min(arg0, arg1…, key=None) 返回最小值;
  • reversed(seq) 返回反向序列;
  • sorted(iter, func=None, key=None, reverse=False) 从迭代器中返回一个排好序的List;
  • sum(seq, init=0) 将序列的所有值叠加起来并加上初始值;
  • zip([it0, it1,… itN]) 把这N个迭代器对应位置的值组成一个元组,直到最短的迭代器没有值为止;

字符串

Python中的字符串实际上由三个类构成:str unicode 和基类 basestring 。Python不支持字符类型,因而单个字符也是字符串。在此再重复一次,Python中的字符串是不可变的,因而不能将切片用于赋值操作符的左边,即不能改变字符串的子串。如:s[2] = ‘a’

Python预定义了如下字符串:string.uppercase string.lowercase string.letters string.digits

Python的连接操作是比较慢的,推荐使用 % 形式的字符串格式化或者把字符串放在 List 中,然后调用 join()方法。如下:

1
2
'%s %s' % ('Spanish', 'Inquisition')
s = ' '.join(('Spanish', 'Inquisition', 'Made Easy'))

笔记中不打算详细讲解 % 格式化字符串的详细用法,它跟 C 的 printf 函数很像,具体参考相关书籍。

Python支持类似于 C 中的源码字符串拼接,相邻的两个字符串字面量自动合并成一个长的字符串 foo = “hello” ‘world!’

当正常字符串和 Unicode 字符串一起使用时,正常字符串被转为 Unicode 字符串,如:‘Hello’ + u’ ’ + ‘World’ + u’!’

Python除了可以使用传统的元组形式的格式化,还可以使用字典形式的格式化:

1
2
'There are %(howmany)d %(lang)s Quotation Symbols' % \
    {'lang':'Python', 'howmany':3}

string 模块中有一个类叫 Template 专门用来处理类似于 PHP 中的变量替换,$形式的变量会被其值替换。substitute()是严格模式,safe_substitute()则在缺少变量时不抛出错误。示例代码如下:

1
2
3
4
from string import Template
s = Template('There are ${howmany} ${lang} Quotation Symbols')
print s.substitute(lang='Python', howmany=3) #缺少其中之一会抛出错误
print s.safe_substitue(lang='Python')

字符串中还有一个原始字符串操作符(r/R),此操作符放在引号的前面,对于字符串中的特殊字符不进行转义。这个特性主要是为了减轻正则表达式字符串的负担。

1
2
3
f = open(r'C:\windows\temp\readme.txt', 'r')
f.readline()
f.close()

Python 字符串还支持 Unicode 字符串,就是在引号前防止 u 或者 U ,而且 ur 可以一起使用。如:

1
2
3
4
u'abc'
u'\u1234'
u'abc\u1234\n'
ur'Hello\nWorld!'

字符串对象方法

简单列举常用的几个字符串对象方法。

  • string.count(str, beg=0, end=len(string)) 返回 str 在 string 中出现的次数,beg 和 end 限定出现的范围;
  • string.decode(encoding=‘UTF-8’, errors=‘strict’) 以指定编码对 string 进行解码;
  • string.encode(encoding=‘UTF-8’, errors=‘strict’) 以指定编码对 string 进行编码;
  • string.endswith(obj, beg=0, end=len(string)) string 中由 beg 和 end 限定的子串是否以 obj 结尾;
  • string.find(str, beg=0, end=len(string)) 在 string 的子串中查找字符串 str ,返回索引,没找到返回 -1 ;
  • string.index(str, beg=0, end=len(string)) 类似于 find 但是在没有找到时抛出异常;
  • string.isalnum() string.isalpha() string.isdecimal() string.isdigit() string.islower() string.isnumeric()
  • string.join(seq) 将序列 seq 中每个元素连接起来,中间用 string 拼接;
  • string.lower() string.upper()
  • string.replace(str1, str2, num=string.count(str1)) 替换 string 中的子串 st1 为 str2 直到达到 num 次;
  • string.rfind(str, beg=0, end=len(string)) 类似于 find ,执行反向搜索;
  • string.rindex(str, beg=0, end=len(string)) 类似于 index ,执行反向搜索;
  • string.lstrip() string.rstrip() string.strip() 消除左右两边的空白;
  • string.split(str="", num=string.count(str)) 将 str 当做分隔符对字符串 string 进行分割直到达到 num 次;
  • string.startswith(obj, beg=0, end=len(string)) 判断 string 的子串是否以 obj 起始;

Python 字符串与 C 字符串的不同之处在于,Python 字符串中可以包含任意数量的 NUL(\0)值,使得它很适合用来 存储二进制数据。

三引号字符串能够原原本本保留字符串中的换行符,特别适合与 HTML 和 SQL 语句。

此笔记不打算讲解 Unicode 字符串,因为涉及到编码和大小端,记住我们目前最常用的编码是 UTF-8 就行了。

列表

列表的最大特点就是可变,并且可以包含任意类型的元素。列表可以用 [] 或者 list() 来创建,可以通过切片操作符来访问列表中的元素。列表有一个重要的特性就是列表解析(List Comprehensions)。可以将列表当做多维数组来使用:

1
2
3
4
mixup_list = [4.0, [1, 'x'], 'beef', -1.9+6j]
mixup_list[1][1] = -64.875
num_list = [43, -1.23, -2, 6.19e5]
num_list[2:4] = [16.0, -49]  #这样不会改变列表的维度

连接操作(+)只能作用于列表而不能是元组或别的类型,连接操作符要求两边的类型一致。

列表的比较执行如下逻辑:对列表中的元素进行比较,如果类型相同则执行值比较,否则认为数字比任何其它类型值小,除此之外别的类型用类型的名字进行比较,最后如果都相等就按照列表的长度进行比较,长的较大,否则长度也相等就是一样大。

List() 和 Tuple() 方法经常用来进行相互之间的转换。它们都能够接受一个迭代器作为参数来生成对应的值。

列表对象方法

  • list.append(obj) 将 obj 添加到列表的末尾;
  • list.count(obj) 返回 obj 在列表中出现的次数;
  • list.extend(seq) 将序列 seq 中的元素添加到列表中;
  • list.index(obj, i=0, j=len(list)) 返回 obj 在列表中的位置,范围必须在 i<=k<j 如果找不到将抛出 ValueError 异常;
  • list.insert(index, obj) 将 obj 插入到列表的 index 位置;
  • list.pop(index=-1) 从列表中移除元素,元素在索引 index 位置或者缺省在最末尾;
  • list.remove(obj) 从列表中一处元素 obj ;
  • list.reverse() 将列表反转此操作将改变列表 list;
  • list.sort(func=None, key=None, reverse=False) 对列表进行排序;

元组

元组跟列表很类似,唯一的区别就是元组是不可变的,而列表是可变的。元组可以用 () 或 tuple() 来创建新对象。如果是空对象,则为 (,) 形式,单元素元组是 (obj,)。由于元组具有不可变性,所以不能改变元组的内容。以下是一些示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
t = (['xyz', 123], 23, -103.4)
t * 2
t = t + ('free', 'easy')
23 in t
123 in t
t[0][1]
t[1:]
str(t)
len(t)
maxt(t)
min(t)
cmpy(t, (['xyz', 123], 23, -103.4, 'free', 'easy'))
list(t)

请牢记:元组的切片值不能放到等号的左边,这是由不变性造成的,如果需要一个可变序列,用 list() 方法。另外一点就是元组中可以包含可变对象,如下:

1
2
t = (['xyz', 123], 23, -103.4)
t[0][1] = ['abc', 'def']

不带括号的逗号分隔的多个对象是元组,通常用于函数返回多个值,同时返回值可以用多个变量来接收。

1
2
3
def foo():
    return 1, 2, 3
a, b, c = foo()

相关模块包括:collections, array, copy 。要对序列进行深度拷贝要使用 copy.deepcopy() ,值得注意的是深度拷贝只会对可变容器对象进行递归拷贝,对于不可变对象依然是只拷贝引用。