python基础教程_学习笔记八:序列_练习与总结_1

python基础教程_学习笔记8:序列_练习与总结_1

序列_练习与总结

每次处理一个字符

任务:

用每次处理一个字符的方式处理字符串。

 

方案:

可以创建一个列表,列表的子项是字符串的字符。python实际上并没有一个特别的类型来对应“字符”并以此和字符串区分开来。可以调用内建的list,用字符串作为参数:

thelist=list(thestring)

也可以不创建一个列表,直接用for语句完成对该字符串的循环遍历:

for c in thestring:

do_something_with(c)

或者使用列表推导中的for来遍历:

results=[do_something_with(c) for c in thestring]

再或者,和列表推导效果完全一样,可以用内建的map函数,每次取得一个字符就调用一次处理函数

results=map(do_something,thestring)

 

讨论:

python中,字符就是长度为1的字符串。可以循环遍历一个字符串,依次访问它的每个字符。也可以用map来实现差不多的功能,只要你愿意每次取到一个字符就调用一次处理函数。还可以用内建的list类型来获取该字符串所有长度为1的子串列表(该字符串的字符)。如果想获得的是该字符串的所有字符的集合,还可以调用sets.Set,并将该字符串作为参数。

 

更多资料:

Library Reference中关于序列的章节;

字符和字符值之间的转换

任务:

将一个字符转化为相应的ASCIIISO)或者Unicode码,或者反其道而行之。

 

方案:

内建函数ordchr擅长的任务:

>>> print ord('b')

98

>>> print chr(98)

b

 

内建函数ord同样也接收长度为1unicode字符串作为参数,此时返回一个unicode的码值,最大到65535。如果想把一个数字的unicode码值转化为一个长度为1unicode字符串,可以使用内建函数unichr

>>> print ord(u'\u2020')

8224

>>> print repr(unichr(8224))

u'\u2020'

 

讨论:

这是个很普通的任务,有时候我们需要将字符转换为ASCII或者Unicode码,有时候则反其道而行之,很常见也很有用。

请注意,新手们常常混淆chr(n)str(n)之间的区别:

>>> print repr(chr(97))

'a'

>>> print repr(str(97))

'97'

chr将一个小整数作为参数并返回对应于ASCII单字符的字符串;

str,以任何整数为参数,返回一个该整数的文本形式的字符串;

 

如果想把一个字符串转化为一个包含各个字符的值的列表,可以同时使用内建的mapord函数:

>>> print map(ord,'signjing')

[115, 105, 103, 110, 106, 105, 110, 103]

 

若想通过一个包含了字符值的列表创建字符串,可以使用”.joinmapchr

>>> print ''.join(map(chr,range(97,102)))

abcde

 

更多资料:

Library Reference中内建函数chrordunichr有关章节。

 

测试一个对象是否是类字符串

任务:

有时候需要测试一个对象,尤其是当你在写一个函数或方法的时候,经常需要测试传入的参数是否是一个字符串(或者更准确地说,这个对象是否具有类似于字符串的行为模式)。

 

解决方案:

可以利用内建的isinstancebasestring来简单快速地检查某个对象是否是字符串或者Unicode对象的方法,如下:

def isAString(anobj):

return isinstance(anobj,basestring)

 

讨论:

很多遇到这个问题的程序员第一反应是进行类型测试:

def isExactlyString(anobj):

return type(anobj) is type(‘’)

然而这个方法非常糟糕,因为它破坏了python强大力量的源泉——平滑的,基于签名的多态机制。很明显Unicode对象无法通过这个测试,用户自己编写的str的子类也不行,甚至任何一种行为表现类似于字符串的用户自定义类型的实例都无法通过测试。

受推荐的内建函数isinstance则好很多,内建类型basestring的存在使得这个方法称为可能。basestringstrUnicode类型的共同基类,任何类字符串的用户自定义类型都应该从基类basestring派生,这样能保证isinstance的测试按照预期工作。本质上basestring是一个“空”的类型,就像object,所以从它派生子类并没有什么开销。

 

不幸的是,isinstance检查方案,对于python标准库中的UserString模块提供的UserString类的实例,完全无能为力。而UserString对象是非常明显的类字符串对象,只不过它不是从basestring派生的,如果想支持这种类型,可以直接检查一个对象的行为是否真的像字符串一样:

def isStringLike(anobj):

try: anobj+’’

except:return False

else:return True

这个isStringLike函数比方案中给出的isAString函数慢且复杂得多,但它的确适用于UserString(以及其他的类字符串的类型)的实例,也适用于strunicode

 

python中通常的类型检查方法是所谓的鸭子判断法:如果它走路像鸭子,叫声也像鸭子,那么对我们的应用而言,就可以认为是鸭子了。isStringLike函数只不过检查了叫声部分,那其实还不够。如果需要检查anobj对象的更多的类字符串特征,可以改造try子句,让它检查更多细节:

try:anobj.lower()+anobj+’’

 

进行类型验证(或任何验证任务)的最具python特色的方法是根据自己的预期去执行任务,在此过程中检测并处理理由并不匹配产生的所有错误和异常。这是一个著名的处理方式,叫做“获得事后原谅总比事先得到许可要容易得多,或简称EAFPtry/except是保证EAFP处理风格的关键工具。

 

更多资料:

Library Reference中内建的isinstancebasestring章节。

字符串对齐

任务:

实现字符串对齐:左对齐、居中对齐或者右对齐。

 

方案:

这正是string对象的ljustrjustcenter方法要解决的问题。每个方法都需要一个参数,指出生成的字符串的宽度,之后返回一个在左端、右端或者两端都添加了空格的字符串拷贝:

>>> print '|','hej'.ljust(20),'|','hej'.rjust(20),'|','hej'.center(20),'|'

| hej                  |                  hej |         hej          |

默认情况下使用空格填充,可以指定第二个参数,指定一个填充字符:

>>> print '|','hej'.ljust(20,'+'),'|','hej'.rjust(20,'+'),'|','hej'.center(20,'+'),'|'

| hej+++++++++++++++++ | +++++++++++++++++hej | ++++++++hej+++++++++ |

 

更多资料:

有关string的方法;

 

去除字符串两端的空格

任务:

获得一个开头或结尾都没有多余空格的字符串。

 

方案:

字符串对象的lstriprstripstrip方法正是为这种任务设计的。这几个方法都需要参数,它们会直接返回一个删除了开头、末尾或者两端的空格的原字符串的拷贝:

>>> x='   hej   '

>>> print '|',x.lstrip(),'|',x.rstrip(),'|',x.strip(),'|'

| hej    |    hej | hej |

 

讨论:

有时候需要给字符串添加一些空格,让其符合预先规定的固定宽度,以完成左右对齐或居中对齐,但有时候也需要从两端移除所有的空格(空白、制表符、换行符等)。这三个方法也可以去除其它字符,只需要提供一个字符串作为这三种方法的参数:

>>> x='   hej   '

>>> print '|',x.lstrip(),'|',x.rstrip(),'|',x.strip(),'|'

| hej    |    hej | hej |

注意:只有开头和结尾的”x”和’y’字符被真正移除了。

>>> print '|'+x.strip('hej')+'|'

|xyxxyy hejyx yyx|

>>> print '|'+x.strip('x')+'|'

|yxxyy hejyx yy|

 

合并字符串

任务:

有一些小的字符串,想把这些字符串合并成一个大字符串。

 

解决:

可以使用字符串操作符join

>>> pieces=['si','gn','ji','ng']

>>> print ''.join(pieces)

signjing

如果想把存储在一些变量中的字符串片段拼接起来,那么使用字符串格式化操作符%会更好些:

largeString=’%s%s something %s yet more’ %(small1,small2,small3)

 

讨论:

python中,+操作符也能够将字符串拼接起来,从而实现类似的功能。

largeString=small1+small2+’ something ‘ + small3 + ‘yet more’

类似地,可以将小字符串序列拼接起来:

largeString=’’

for piece in pieces:

largeString+=piece

或者,用完全等同但却更加漂亮和紧凑的方法:

import operator

largeString=reduce(operator.add,pieces,’’)

 

不过,上面给出的方法都有很多值得推敲的地方:

python中的字符串对象是无法改变的。任何对字符串的操作,包括字符串拼接,都将产生一个新的字符串对象,而不是修改原有的对象。因此,拼接n个字符串将涉及创建并丢弃n-1个中间结果。当然,不创建中间结果的操作会有更佳的性能,但往往不能一步到位地取得最终结果。

 

如果有少量字符串(尤其是那些绑定到变量上的)需要拼接,甚至有时还需要添加一些额外的信息,python的字符串格式化操作符%通常是更好的选择。性能对这种操作完全不是一个问题。和使用多个+操作符相比,%操作符还有一些其它的潜在优点。一旦习惯了它,%也会让你的代码的可读性更好。也无须再对所有的非字符串(如数字)部分调用str,因为格式指定符%已经暗中做完了这些工作。另一个优点是,还可以使用除%s之外的其他格式指定符,这样就可以实现更多的格式要求,比如将浮点数转化为字符串的表示时,可以控制它的有效位数。

 

什么是“序列”?

Python并没有一个特别的类型叫做sequence,但序列是python中一个非常有用的术语。序列,简单地讲,是一个可迭代的容器,可以从中取出一定数目的子项,也可以一次取出一个,而且还支持索引、切片,还可以传递内建函数len(返回容器中子项的数目)。

 

通常,一个对象即使不支持索引、切片和len,只要具有一次获得一项的迭代能力,对应用而言就已经够用了。这叫做可迭代对象(或者,如果我们把范围限定在拥有有限子项的情况下,那就叫做边界可迭代对象)。可迭代对象不是序列,而是如字典(迭代操作将以任意顺序每次取得一个key)、文件对象(迭代操作将给出文本文件的行数)等,还有其他一些,包括迭代器和生成器等。任何可迭代对象都能用在for循环语句以及一些等价的环境中。

 

当一个序列中包含了很多的小字符串的时候,性能就变成了一个很现实的问题。在内部使用了+或者+=(和内建函数reduce作用相同,但是更漂亮)的循环所需要的时间跟需要累加的字符数的平方成正比,因为分配空间并填充一个大字符串所需要的时间大致正比于该字符串的长度。幸好python提供了另一个更好的选择。对于字符串对象sjoin方法,可以传入一个字符串序列作为参数,将返回一个由字符串序列中所有子项字符串拼接而成的大字符串,而且这个过程中只使用了一个s的拷贝用于串接所有的子项。举个例子,’’.join(pieces)pieces中所有的子项一口吞下,而无需产生子项之间的中间结果,再比如,’,’.join(pieces)拼接了所有的子项字符串,并在拼接的两项之间插入了一个逗号和空格。这是一种快速、整洁、优雅且兼具良好可读性的合并大字符串的方法。

>>> pieces=['si','gn','ji','ng']

>>> print ', '.join(pieces)

si, gn, ji, ng

 

但有时候并不是所有的类型在一开始就已经就位,比如数据可能来自于输入或计算,这时可以使用一个list作为中间数据结构来容纳它们。python字符串处理的各种技巧和方法中,这是最重要的一条:很多python程序效能低下的原因是由于它们使用了++=来创建大字符串。因此,一定要提醒自己永远不要使用那种方法,而使用推荐的’’.join方法。虽然不同的python版本和一些编辑器已大幅度减少+=带来的性能损失,但依然需要强调的是:’’.join依然是最值得选择的方式。

 

更多资料:

字符串方法、字符串格式化操作及operator模块;

将字符串住字符或逐词反转

任务:

把字符串逐字符或逐词反转过来。

 

解决:

字符串无法改变,所以,反转一个字符串需要创建一个拷贝。最简单的方法是使用一种“步长”为-1的的特殊的切片方法,指引可立即产生一个完全反转的效果:

revchars=astring(::-1)

如果要按照单词来反转字符串,需要先创建一个单词的列表,将这个列表反转,然后再用join方法将其合并,并在相邻两词之间都插入一个空格:

revwords=astring.split()

revwords.reverse()

revwords=’ ’.join(revwords)

或者,如果喜欢简练而紧凑的“一行解决”的代码:

revwords=’ ’.join(astring.split()[::-1])

 

如果想逐词反转但又同时不改变原先的空格,可以用正则表达式来分隔原字符串:

import re

revwords=re.split(r’(\s+)’,astring)

revwords.reverse()

revwords=’’.join(revwords)

注意:最后的join操作符要使用空字符串,因为空格分隔符已经被保持在rewords列表中了。

讨论:

也可以使用内建函数reversedreversed返回一个迭代器,该对象可以被用于循环或者传递给其他的“累加器”,但它并不是一个已经完成的字符串。

至于逐字符反转,astring[::-1]仍然是最好的方式。

 

更多资料:

序列的切片、类型及内建的reversed方法。