它们的成员有序排列的,而且能够经过下标偏移量访问到它的一个或者几个成员,称为序列,包括下面这些:字符串(普通字符串和unicode 字符串),列表,和元组类型。html
首先咱们来熟悉一下适用于全部序列类型的操做符和内建函数(BIFs),python
简介git
操做符程序员
内建函数web
内建函数(若是可用)正则表达式
特性(若是可用)算法
相关模块(若是可用)shell
序列类型有着相同的访问模式:它的每个元素能够经过指定一个偏移量的方式获得。而多个元素能够经过切片操做的方式一次获得。数据库
下标偏移量是从0 开始到 总元素数-1 结束 -- 之因此要减一是由于咱们是从0 开始计数的。编程
标准类型操做符(参见4.5 节)通常都能适用于全部的序列类型。固然,若是做复合类型的对象比较的话,这样说可能须要有所保留,不过其余的操做绝对是彻底适用的。
表6.1 列出了对全部序列类型都适用的操做符。操做符是按照优先级从高到底的顺序排列的。
链接操做符( + )
这个操做符容许咱们把一个序列和另外一个相同类型的序列作链接。语法以下:
sequence1 + sequence2
注意,这个操做不是最快或者说最有效的。对字符串来讲,这个操做不如把全部的子字符串放到一个列表或可迭代对象中,而后调用一个join方法来把全部的内容链接在一块儿节约内存;相似地,对列表来讲,咱们推荐读者用列表类型的extend()方法来把两个或者多个列表对象合并.当你须要简单地把两个对象的内容合并,或者说不能依赖于可变对象的那些没有返回值(实际上它返回一个None)的内建方法来完成的时候时,链接操做符仍是很方便的一个选择。
重复操做符 ( * )
当你须要须要一个序列的多份拷贝时,重复操做符很是有用,它的语法以下:
sequence * copies_int
copies_int 必须是一个整数(1.6 节里面有讲到,不能是长整数).像链接操做符同样,该操做符返回一个新的包含多份原对象拷贝的对象。
切片操做符 ( [], [:], [::] )
序列容许经过指定下标的方式来得到某一个数据元素,或者经过指定下标范围来得到一组序列的元素.这种访问序列的方式叫作切片。访问某一个数据元素的语法以下:
sequence[index]
偏移量能够是正值,范围从0 到偏移最大值(比序列长度少一),用len()函数能够获得序列长度,实际范围: 0 <= inde <= len(sequece)-1 ;
使用负索引,范围是 -1 到序列的负长度,-len(sequence), -len(sequence) <= index <= -1;
正负索引的区别在于正索引以序列的开始为起点,负索引以序列的结束为起点.试图访问一个越界的索引会引起一个以下的异常:
>>> names = ('Faye', 'Leanna', 'Daylen') >>> print names[4] Traceback (most recent call last): File "<stdin>", line 1, in ? IndexError: tuple index out of range
由于Python 是面向对象的,因此你能够像下面这样直接访问一个序列的元素(不用先把它赋值给一个变量):
>>> print ('Faye', 'Leanna', 'Daylen')[1] Leanna
这个特性在你调用一个返回值是序列类型的函数,而且你只对返回的序列中的一个或某几个元素感兴趣时特别有用.
如何一次获得多个元素呢?
sequence[starting_index:ending_index]
起始索引和结束索引都是可选的,若是没有提供或者用None 做为索引值,切片操做会从序列的最开始处开始,或者直到序列的最末尾结束.
用步长索引来进行扩展的切片操做
多出来的第三个索引值被用作步长参数。 Python 的虚拟机里面其实很早就有了扩展切片操做,eg:
>>> s = 'abcdefgh' >>> s[::-1] # 能够视做"翻转"操做 'hgfedcba' >>> s[::2] # 隔一个取一个的操做 'aceg'
切片索引的更多内容
切片索引的语法要比简单的单一元素索引灵活的多。开始和结束素引值能够超过字符串的长度。简单地说,即便用100 来做为一个长度不到100 的序列的结束索引也不会有什么错误,例子以下:
>>> ('Faye', 'Leanna', 'Daylen')[-100:100] ('Faye', 'Leanna', 'Daylen')
有一个字符串,咱们想经过一个循环按照这样的形式显示它:每次都把位于最后的一个字符砍掉,下面是实现这个要求的一种方法:
>>> s = 'abcde' >>> i = -1 >>> for i in range(-1, -len(s), -1): ... print s[:i] ... abcd abc ab a
但是,该如何在第一次迭代的时候显示整个字符串呢?是否有一种方法能够不用在整个循环以前加入一个额外的print 语句呢?以负数做为索引的例子里是不能解决这个的方法,由于-1 已是“最小”的索引了。
可使用另外一个小技巧:用None 做为索引值,这样一来就能够知足你的须要,好比说,在你想用一个变量做为索引来从第一个到遍历最后一个元素的时候:
>>> s = 'abcde' >>> for i in [None] + range(-1, -len(s), -1): ... print s[:i] ... abcde abcd abc ab a
如今这个程序符合咱们的要求了。彷佛还能够先建立一个只包含None 的列表,而后用extend()函数把range()的输出添加到这个列表,或者先创建range()输出组成的列表而后再把None 插入到这个列表的最前面,而后对这个列表进行遍历,可是可变对象的内建函数extend()根本就没有返回值,因此这个方法是行不通的:
>>> for i in [None].extend(range(-1, -len(s), -1)): ... print s[:i] ... Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: iteration over non-sequence
这个错误发生的缘由是[None].extend(...)函数返回None , None 既不是序列类型也不是可迭代对象.
序列自己就内含了迭代的概念,由于迭代这个概念就是从序列,迭代器,或者其余支持迭代操做的对象中泛化得来的。因为Python 的for 循环能够遍历全部的可迭代类型,在(非纯序列对象上)执行for 循环时就像在一个纯序列对象上执行同样。并且Python 的不少原来只支持序列做为参数的内建函数如今也开始支持迭代器或者或类迭代器了.咱们把这些类型统称为"可迭代对象".在这一章里咱们会详细的讨论跟序列关系紧密的内建函数(BIF).
类型转换
内建函数list(),str()和tuple()被用作在各类序列类型之间转换。你能够把它们理解成其余语言里面的类型转换,可是并无进行任何的转换。这些转换其实是工厂函数(在第4章介绍),将对象做为参数,并将其内容(浅)拷贝到新生成的对象中.表6.2 列出了适用于序列类型的转换函数。
咱们又用了一次“转换”这个词。不过,为何Python 里面不简单地把一个对象转换成另外一个对象呢?回过头看一下第4 章就会知道,一旦一个Python 的对象被创建,咱们就不能更改其身份或类型了.若是你把一个列表对象传给list()函数,便会建立这个对象的一个浅拷贝,而后将其插入新的列表中。一样地,在作链接操做和重复操做时,咱们也会这样处理。所谓浅拷贝就是只拷贝了对对象的索引,而不是从新创建了一个对象!若是你想彻底的拷贝一个对象(包括递归,若是你的对象是一个包含在容器中的容器),你须要用到深拷贝,关于浅拷贝和深拷贝的更多信息会在本章的末尾讲到。
str()函数在须要把一个对象的可打印信息输出时特别有用,不只仅是对序列类型,对其余类型的对象一样如此.
Unicode()是str()函数的unicode 版本,它跟str()函数基本同样.
list()和tuple()函数在列表类型和元组类型的互换时很是有用.不过,虽然这些函数也适用于string类型(由于string 类型也是序列的一种),可是在string 类型上应用tuple()和list()函数却得不到咱们一般但愿的结果.
Operational
咱们将分别在每一个序列的章节里面提供使用这些函数的例子.
Python 里面单引号和双引号的做用是相同的。Python 用"原始字符串"操做符来建立直接量字符串,因此再作区分就没什么意义了.
几乎全部的Python 应用程序都会某种方式用到字符串类型.字符串是一种直接量或者说是一种标量,这意味着Python 解释器在处理字符串时是把它做为单一值而且不会包含其余Python 类型的。字符串是不可变类型,就是说改变一个字符串的元素须要新建一个新的字符串.
根据在2.2 章节里面对类型和类的概念进行的统一,Python 实际上有3 类字符串.一般意义的字符串(str)和Unicode 字符串(unicode)实际上都是抽象类basestring 的子类.这个basestring 是不能实例化的,若是你试图实例化一个basestring 类,你会获得如下报错信息:
>>> basestring('foo') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: The basestring type cannot be instantiated
字符串的建立和赋值
建立一个字符串就像使用一个标量同样简单,固然你也能够把str()做为工厂方法来建立一个字符串并把它赋值给一个变量:
>>> aString = 'Hello World!' # 使用单引号 >>> anotherString = "Python is cool!" # 使用双引号 >>> print aString # print 不带引号的 Hello World! >>> anotherString # 不是进行print 操做,带有引号 'Python is cool!' >>> s = str(range(4)) # 把一个列表转换成一个字符串 >>> s '[0, 1, 2, 3]'
如何访问字符串的值(字符和子串)
Python 里面没有字符这个类型,而是用长度为1 的字符串来表示这个概念,固然,这其实也是一个子串。
如何改变字符串
你能够经过给一个变量赋值(或者重赋值)的方式“更新”一个已有的字符串。
>>> aString = aString[:6] + 'Python!' >>> aString 'Hello Python!' >>> aString = 'different string altogether' >>> aString 'different string altogether'
跟数字类型同样,字符串类型也是不可变的,因此你要改变一个字符串就必须经过建立一个新串的方式来实现。也就是说你不能只改变一个字符串的一个字符或者一个子串,然而,经过拼凑一个旧串的各个部分来获得一个新串是被容许的,正如上面你看到的那样.
如何删除字符和字符串
再重复一遍,字符串是不可变的,因此你不能仅仅删除一个字符串里的某个字符,你能作的是清空一个空字符串,或者是把剔除了不须要的部分后的字符串组合起来造成一个新串。假设你想要从"Hello World!"里面删除小写的'l'
>>> aString = 'Hello World!' >>> aString = aString[:3] + aString[4:] >>> aString 'Helo World!'
经过赋一个空字符串或者使用del 语句来清空或者删除一个字符串:
>>> aString = '' >>> aString '' >>> del aString
在大部分应用程序里,没有必要显式的删除字符串。定义这个字符串的代码最终会结束,那时Python 会自动释放这些字符串.
在第4 章里面,咱们介绍了一些适用于包括标准类型在内的大部分对象的操做符,在这里再看一下这些其中的一些操做符是怎样做用于字符串类型的,下面是几个简单的例子:
>>> str1 = 'abc' >>> str2 = 'lmn' >>> str3 = 'xyz' >>> str1 < str2 True >>> str2 != str3 True >>> str1 < str3 and str2 == 'xyz' False
在作比较操做的时候,字符串是按照ASCII 值的大小来比较的.
切片( [ ] 和 [ : ] )
正向索引
反向索引
默认索引
接下来以字符串'abcd'为例子.能够用长度操做符来确认该字符串的长度是4:
>>> aString = 'abcd' >>> len(aString) 4
用一个参数来调用切片操做符结果是一个单一字符,而使用一个数值范围(用':')做为参数调用切片操做的参数会返回一串连续地字符.再强调一遍,对任何范围[start:end],咱们能够访问到包括start 在内到end(不包括end)的全部字符,
>>> aString[0] 'a' >>> aString[1:3] 'bc' >>> aString[2:4] 'cd' >>> aString[4] Traceback (innermost last): File "<stdin>", line 1, in ? IndexError: string index out of range
使用不在容许范围(本例中是0 到3)内的索引值会致使错误。上面的aString[2:4]却并无出错,那是由于实际上它返回的是索引值2 和3 的值。可是直接拿4 做为索引访问是不被容许的。
在进行反向索引操做时,是从-1 开始,向字符串的开始方向计数,到字符串长度的负数为索引的结束:
>>> aString[-1] 'd' >>> aString[-3:-1] 'bc' >>> aString[-4] 'a'
若是开始索引或者结束索引没有被指定,则分别以字符串的第一个和最后一个索引值为默认值。
>>> aString[2:] 'cd' >>> aString[1:] 'bcd' >>> aString[:-1] 'abc' >>> aString[:] 'abcd'
注意:起始/结束索引都没有指定的话会返回整个字符串.
【其实感受没有指定的话,应该是最后一个索引值+1,由于若是以最后一个索引值为默认的话,应该不会显示d,按照切片的定义来讲】
成员操做符(in ,not in)
成员操做符用于判断一个字符或者一个子串(中的字符)是否出如今另外一个字符串中。注意,成员操做符不是用来判断一个字符串是否包含另外一个字符串的【谁能告诉我这个啥意思,示例里面不就是判断一个字符串包含一个字符串了吗】 ,这样的功能由find()或者index()(还有它们的兄弟:rfind()和rindex())函数来完成)
下面是一些字符串和成员操做符的例子.
>>> 'bc' in 'abcd' True >>> 'n' in 'abcd' False >>> 'nm' not in 'abcd' True
在例6.1 里面,咱们会用到下面这些string 模块预约义的字符串:
>>> import string >>> string.ascii_uppercase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> string.ascii_lowercase 'abcdefghijklmnopqrstuvwxyz' >>> string.ascii_letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> string.digits '0123456789'
例 6.1 标识符检查 (idcheck.py)标识符合法性检查。这个小例子只检查长度大于等于2 的标识符.
# 导入string 模块而且预约义了两个字符串 import string alphas = string.ascii_letters + '_' nums = string.digits inp = input('Identifier to test? ') if len(inp) > 1: if inp[0] not in alphas: print ("invalid first symbol") else: for otherChar in inp[1:]: if otherChar not in alphas +nums: print ("invalid other char") break print ("ok") else: print ("len > 2")
这个例子还展现了字符串链接符( + )的使用。
核心提示: 性能
从性能的的角度来讲,把重复操做做为参数放到循环里面进行是很是低效的.
while i < len(myString): print 'character %d is:', myString[i]
上面的循环操做把大把的时间都浪费到了重复计算字符串myString 的长度上了.每次循环迭代都要运行一次这个函数.若是把这个值作一次保存,咱们就能够用更为高效的方式重写咱们的循环操做.
length = len(myString) while i < length: print 'character %d is:', myString[i]
这个方法一样适用于上面的例6.1
for otherChar in myInput[1:]: if otherChar not in alphas + nums:
在这个if 里面执行了合并两个字符串的操做。被合并的这两个字符串从始至终就没变过,而每次都会从新进行一次计算.若是先把这两个字符串存为一个新字符串,咱们就能够直接引用这个字符串而不用进行重复计算了。
alphnums = alphas + nums for otherChar in myInput[1:]: if otherChar not in alphnums:
这段程序并无想到Python 的关键字,而这些都是做为保留字,不容许用作标识符的.咱们把这做为课后做业留给读者(见练习6-2)。
链接符( + )
运行时刻字符串链接,咱们能够经过链接操做符来从原有字符串得到一个新的字符串.eg:
>>> 'Spanish' + 'Inquisition' 'SpanishInquisition' >>> 'Spanish' + ' ' + 'Inquisition' 'Spanish Inquisition' >>> s = 'Spanish' + ' ' + 'Inquisition' + ' Made Easy' >>> s 'Spanish Inquisition Made Easy' >>> import string >>> string.upper(s[:3] + s[20]) # archaic (see below) 'SPAM'
最后一个例子展现了用一个字符串s 的两个切片来构成一个新串的操做,,该方法负责把字符串的全部字符都变为大写。
String 模块的方法是在Python1.6 里面添加进来的,因此这个操做也能够用最后一个字符串的一个单一方法调用来完成。如今已经没有必要导入string 模块了,除非你须要访问该模块本身定义的字符串常量。出于性能方面方面,建议不要用string 模块。缘由是Python 必须为每个参加链接操做的字符串分配新的内存,包括新产生的字符串。推荐使用字符串格式化操做符(%),或者把全部的字符串放到一个列表中去,而后用一个join()方法来把它们链接在一块儿。
>>> '%s %s' % ('Spanish', 'Inquisition') 'Spanish Inquisition' >>> >>> s = ' '.join(('Spanish', 'Inquisition', 'Made Easy')) >>> s 'Spanish Inquisition Made Easy' >>> >>> # no need to import string to use string.upper(): >>> ('%s%s' % (s[:3], s[20])).upper() 'SPAM'
编译时字符串链接
上面的语法在运行时字符串链接的加法操做,这个用法是很是标准的。Python 中还有一种并非常常用到,更像是一种程序员的习惯用法的语法.Python 的语法容许你在源码中把几个字符串连在一块儿写,以此来构建新字符串:
>>> foo = "Hello" 'world!' >>> foo 'Helloworld!'
经过这种方法,你能够把长的字符串分红几部分来写,而不用加反斜杠。
你能够在一行里面混用两种分号。这种写法的好处是你能够把注释也加进来,以下:
>>> f = urllib.urlopen('http://' # protocol ... 'localhost' # hostname ... ':8000' # port ... '/cgi-bin/friends2.py') # file
如你所想,下面就是urlopen()方法所获得的真实输入:
>>> 'http://' 'localhost' ':8000' '/cgi-bin/friends2.py' 'http://localhost:8000/cgi-bin/friends2.py'
普通字符串转化为Unicode 字符串
若是把一个普通字符串和一个Unicode 字符串作链接处理,Python 会在链接操做前先把普通字符串转化为Unicode 字符串:
>>> 'Hello' + u' ' + 'World' + u'!' u'Hello World!'
重复操做符( * )
重复操做符建立一个包含了原有字符串的多个拷贝的新串:
>>> 'Ni!' * 3 'Ni!Ni!Ni!' >>> '*'*40 '****************************************' >>> print '-' * 20, 'Hello World!', '-' * 20 -------------------- Hello World! -------------------- >>> who = 'knights' >>> who * 2 'knightsknights' >>> who 'knights'
像其余的标准操做符同样,原变量是不被修改的,就像上面最后一个例子所示。
只适用于字符串类型,而且支持全部printf()式的格式化操做.语法以下:
左边的format_string 里面一般会在printf()函数的第一个参数里面见到的同样:包含%的格式化字符串.arguments_to_convert 参数是你要转化、显示的变量,对应于你送给prinf 的其余参数.
表6.4 列出了可用的各类符号.
Python 支持两种格式的输入参数:元组和字典。字典实际上是一个哈希键-值对的集合。这种形式里面,key 是做为格式字符串出现,相对应的value 值做为参数在进行转化时提供给格式字符串.
格式字符串既能够跟print 语句一块儿用来向终端用户输出数据,又能够用来合并字符串造成新字符串,并且还能够直接显示到GUI(Graphical User Interface)界面上去.
表6.5 格式化操做符辅助指令
如下是一些使用格式字符串的例子:
十六进制输出: >>> "%x" % 108 '6c' >>> "%X" % 108 '6C' >>> "%#X" % 108 '0X6C' >>> "%#x" % 108 '0x6c' 浮点数和科学记数法形式输出: >>> '%f' % 1234.567890 '1234.567890' >>> '%.2f' % 1234.567890 '1234.57' >>> '%E' % 1234.567890 '1.234568E+03' >>> '%e' % 1234.567890 '1.234568e+03' >>> '%g' % 1234.567890 '1234.57' >>> '%G' % 1234.567890 '1234.57' >>> "%e" % (1111111111111111111111L) '1.111111e+21' 整数和字符串输出: >>> "%+d" % 4 '+4' >>> "%+d" % -4 '-4' >>> "we are at %d%%" % 100 'we are at 100%' >>> 'Your host is: %s' % 'earth' 'Your host is: earth' >>> 'Host: %s\tPort: %d' % ('mars', 80) 'Host: mars Port: 80' >>> num = 123 >>> 'dec: %d/oct: %#o/hex: %#X' % (num, num, num) 'dec: 123/oct: 0173/hex: 0X7B' >>> "MM/DD/YY = %02d/%02d/%d" % (2, 15, 67) 'MM/DD/YY = 02/15/67' >>> w, p = 'Web', 'page' >>> 'http://xxx.yyy.zzz/%s/%s.html' % (w, p) 'http://xxx.yyy.zzz/Web/page.html'
上面的例子都是使用的元组类型的参数做转换.下面咱们将把字典类型的参数提供给格式化操做符.
>>> 'There are %(howmany)d %(lang)s Quotation Symbols' % \ ... {'lang': 'Python', 'howmany': 3} 'There are 3 Python Quotation Symbols'
字符串格式化操做符是一个很是有用的调试工具。事实上,全部的Python 对象都有一个字符串表示形式(经过repr()函数,'' 或str()函数来展示).print 语句自动为每一个对象调用str()函数.更好的是,在定义本身的对象时,你能够利用"钩子"为你的对象建立字符串表达形式. 这样,repr(),str()或`` 或者print 被调用时,就能够得到一个适当的字符串描述信息.即便在坏的不能再坏的状况下,repr()或者str()也不能显示一个对象的信息时,Pythonic 方式的默认作法最起码能给你返回想以下格式的信息:
<... something that is useful ...>.
程序员会偶尔出现遗漏转换类型符号的错误,好比说,用了%(lang)而不是正确的%(lang)s.为了保证字符串被正确的转换,程序员必须明确的记住转换类型参数,好比究竟是要转成字符串,整数仍是其余什么类型.
新式的字符串模板的优点是不用去记住全部的相关细节的,而是像如今shell 风格的脚本语言里面那样使用美圆符号($).
因为新式的字符串Template 对象的引进使得string 模块又从新活了过来,Template 对象有两个方法,substitute()和safe_substitute().前者更为严谨,在key 缺乏的状况下它会报一个KeyError 的异常出来,然后者在缺乏key 时,直接原封不动的把字符串显示出来.
>>> from string import Template >>> s = Template('There are ${howmany} ${lang} Quotation Symbols') >>> print s.substitute(lang='Python', howmany=3) There are 3 Python Quotation Symbols >>> print s.substitute(lang='Python') Traceback (most recent call last): File "<stdin>", line 1, in ? File "/usr/local/lib/python2.4/string.py", line 172, in substitute return self.pattern.sub(convert, self.template) File "/usr/local/lib/python2.4/string.py", line 162, in convert val = mapping[named] KeyError: 'howmany' >>> print s.safe_substitute(lang='Python') There are ${howmany} Python Quotation Symbols
新式的字符串模板是从Python2.4 开始加入的,更多信息请查阅Python 类库手册和PEP292.
关于原始字符串的目的,是为了对付那些在字符串中出现的特殊字符(下面的小节会介绍这些特殊字符)。在原始字符串里,全部的字符都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。
原始字符串的这个特性让一些工做变得很是的方便,好比正则表达式的建立(详见文档的re模块).正则表达式是一些定义了高级搜索匹配方式的字符串,一般是由表明字符,分组、匹配信息、变量名、和字符类等的特殊符号组成。正则表达式模块已经包含了足够用的符号。但当你必须插入额外的符号来使特殊字符表现的像普通字符的时候,你就陷入了“字符数字”的泥潭!这时原始字符串就会派上用场了.除了原始字符串符号(引号前面的字母"r")之外,原始字符串跟普通字符串有着几乎彻底相同的语法.
这个'r'能够是小写也能够是大写,惟一的要求是必须紧靠在第一个引号前.
在三个例子的第一个例子里面,咱们须要一个反斜杠加一个'n'来而不是一个换行符.:
>>> '\n' '\n' >>> print '\n' >>> r'\n' '\\n' >>> print r'\n' \n
接下来的例子里,咱们打不开咱们的README 文件了,为何?由于'\t'和'\r'被当成不在咱们的文件名中的特殊符号,但它们实际上文件路径的中4 个独立的字符.
>>> f = open('C:\windows\temp\readme.txt', 'r') Traceback (most recent call last): File "<stdin>", line 1, in ? f = open('C:\windows\temp\readme.txt', 'r') IOError: [Errno 2] No such file or directory: 'C:\\win- dows\\temp\readme.txt' >>> f = open(r'C:\windows\temp\readme.txt', 'r') >>> f.readline() 'Table of Contents (please check timestamps for last update!)\n' >>> f.close()
最后咱们要找一对原始的\n 字符而不是换行。为了找到它,咱们使用了一个简单的正则表达式,它的做用是查找一般被用来表示空白字符的反斜线-字符对(backslash-characterpairs)。
>>> import re >>> m = re.search('\\[rtfvn]', r'Hello World!\n') >>> if m is not None: m.group() ... >>> m = re.search(r'\\[rtfvn]', r'Hello World!\n') >>> if m is not None: m.group() ... '\\n'
Unocide 字符串操做符,大写的(U)和小写的(u)是和 Unicode 字符串一块儿被引入的. 它用来把标准字符串或者是包含Unicode 字符的字符串转换成彻底地Unicode 字符串对象。关于Unicode 字符串的进一步信息在6.7.4 节有详细介绍.另外,字符串方法(见6.6节)和正则表达式引擎也支持Unicode.下面是几个例子:
u'abc' U+0061 U+0062 U+0063 u'\u1234' U+1234 u'abc\u1234\n' U+0061 U+0062 U+0063 U+1234 U+0012
Unicode 操做符也能够接受原始Unicode 字符串, 只要咱们将Unicode 操做符和原始字符串操做符链接在一块儿就能够了. 注意:Unicode 操做符必须出如今原始字符串操做符前面.
ur'Hello\nWorld!'
cmp() 内建的cmp()函数也根据字符串的ASCII 码值进行比较.
>>> str1 = 'abc' >>> str2 = 'lmn' >>> str3 = 'xyz' >>> cmp(str1, str2) -11 >>> cmp(str3, str1) 23 >>> cmp(str2, 'lmn') 0
内建函数len()返回字符串的字符数.
max() and min() >>> str2 = 'lmn' >>> str3 = 'xyz' >>> max(str2) 'n' >>> min(str3) 'x'
虽然max()和min()函数对其余的序列类型可能更有用,但对于string 类型它们能很好地运行,返回最大或者最小的字符,(按照ASCII 码值排列),下面是几个例子:
>>> min('ab12cd') '1' >>> min('AB12CD') '1' >>> min('ABabCDcd') 'A'
enumerate()
>>> s = 'foobar' >>> for i, t in enumerate(s): ... print i, t ... 0 f 1 o 2 o 3 b 4 a 5 r
zip()
>>> s, t = 'foa', 'obr' >>> zip(s, t) [('f', 'o'), ('o', 'b'), ('a', 'r')]
raw_input()
内建的raw_input()函数使用给定字符串提示用户输入并将这个输入返回,eg:
>>> user_input = raw_input("Enter your name: ") Enter your name: John Doe >>> user_input 'John Doe' >>> len(user_input) 8
Python 里面没有C 风格的结束字符NULL,你输入多少个字符,len()函数的返回值就是多少.
str() and unicode()
str()和unicode()函数都是工厂函数,就是说产生所对应的类型的对象.它们接受一个任意类型的对象,而后建立该对象的可打印的或者Unicode 的字符串表示. 它们和basestring 均可以做为参数传给isinstance()函数来判断一个对象的类型.
>>> isinstance(u'\0xAB', str) False >>> not isinstance('foo', unicode) True >>> isinstance(u'', basestring) True >>> not isinstance('foo', basestring) False
chr(), unichr(), and ord()
chr()函数用一个范围在range(256)内的(就是0 到255)整数作参数,返回一个对应的字符.
unichr()跟它同样,只不过返回的是Unicode 字符,unichr()的参数范围依赖于你的Python 是如何被编译的.若是是配置为USC2 的Unicode,那么它的容许范围就是range(65536) 或者说0x0000-0xFFFF; 若是配置为UCS4 , 那么这个值应该是range(1114112)或者0x000000-0x110000.若是提供的参数不在容许的范围内,则会报一个ValueError 的异常。
ord()函数是chr()函数(对于8 位的ASCII 字符串)或unichr()函数(对于Unicode 对象)的配对函数,它以一个字符(长度为1 的字符串)做为参数,返回对应的ASCII 数值,或者Unicode数值,若是所给的Unicode 字符超出了你的Python 定义范围,则会引起一个TypeError 的异常。
>>> chr(65) 'A' >>> ord('a') 97 >>> unichr(12345) u'\u3039' >>> chr(12345) Traceback (most recent call last): File "<stdin>", line 1, in ? chr(12345) ValueError: chr() arg not in range(256) >>> ord(u'\ufffff') Traceback (most recent call last): File "<stdin>", line 1, in ? ord(u'\ufffff') TypeError: ord() expected a character, but string of length 2 found >>> ord(u'\u2345') 9029
这些方法实现了string 模块中的大部分方法,表6.6 列出了目前字符串内建支持的方法,全部这些方法都包含了对Unicode 的支持,有一些甚至是专门用于Unicode 的.
几个使用字符串方法的例子:
>>> quest = 'what is your favorite color?' >>> quest.capitalize() 'What is your favorite color?' >>> quest.center(40) ' what is your favorite color? ' >>> quest.count('or') 2 >>> quest.endswith('blue') False >>> quest.endswith('color?') True >>> quest.find('or', 30) -1 >>> quest.find('or', 22) 25 >>> quest.index('or', 10) 16 >>> ':'.join(quest.split()) 'what:is:your:favorite:color?' >>> quest.replace('favorite color', 'quest') 'what is your quest?' >>> quest.upper() 'WHAT IS YOUR FAVORITE COLOR?'
上面最复杂的例子是有split()和join()函数的那个.首先咱们在string 上调用split()函数,没有用参数,也就是说以空格做为分隔符分隔字符串,而后咱们以这个包含单词的列表作参数调用join()方法把这些单词用一个新的分隔符冒号从新串在一块儿,注意,咱们首先用split()函数把string 切片成一个列表,而后咱们在字符串':'上应用join()方法把这个列表从新链接成一个字符串.
一个反斜线加一个单一字符能够表示一个特殊字符,一般是一个不可打印的字符,这就是咱们上面讨论的特殊字符。
除了一般用的特殊字符,好比换行符(\n),tab 符(\t)以外,也能够直接用ASCII 码值来标示特殊字符:\000 或者\xXX,分别对应字符的八进制和十六进制ASCII 码值,下面分别是十进制,八进制和十六进制的0,65,和255:
特殊字符,包括反斜杠转义的那些均可以像普通字符同样存储到Python 的字符串中.跟C 字符串的另外一个不一样之处是Python 的字符串并非以NUL(\000)做为结束符的.NUL 跟其余的反斜杠转义字符没什么两样.事实上,一个字符串中不只能够出现NUL 字符,并且还能够出现不止一次,在字符串的任意位置均可以。表6.7 列出了被大部分Python 版本支持的转义字符.
如上所述,就像使用连字符来让一行的内容持续到下一行同样,能够用显式定义八进制或者十六进制的ASCII 码的方式定义特殊字符,合法的ASCII 码值范围是从0 到255(八进制的是0177,十六进制是0XFF).
Table 6.7 反斜杠开头的转义字符
控制字符的一个做用是用作字符串里面的定界符,在数据库或者web 应用中,大多数的可打印字符都是被容许用在数据项里面的,就是说可打印的字符不适合作定界符.用可打印的字符串好比冒号(:)来做定界符,将会很难分辨一个字符究竟是数据仍是定界符.并且还会限定你能用在数据项里面的字符数量,而这不是你想要的.一个一般的解决方法是,使用那些不常用的,不可打印的ASCII 码值来做为定界符,它们是很是完美的定界符,这样一来诸如冒号这样的可打印字符就能够解脱出来用在数据项中了.
虽然你能够用单引号或者双引号来定义字符串,可是若是你须要包含诸如换行符这样的特殊字符时,单引号或者双引号就不是那么方便了。Python 的三引号就是为了解决这个的,它容许一个字符串跨多行,字符串中能够包含换行符、制表符以及其余特殊字符.三引号的语法是一对连续的单引号或者双引号(一般都是成对的用):
>>> hi = '''hi there''' >>> hi # repr() 'hi\nthere' >>> print hi # str() hi there
三引号让程序员从引号和特殊字符串的泥潭里面解脱出来,自始至终保持一小块字符串的格式是所谓的WYSIWYG(所见即所得)格式的。
一个典型的用例是,当你须要一块HTML 或者SQL 时,这时用字符串组合,特殊字符串转义将会很是的繁琐.
errHTML = ''' <HTML><HEAD><TITLE> Friends CGI Demo</TITLE></HEAD> <BODY><H3>ERROR</H3> <B>%s</B><P> <FORM><INPUT TYPE=button VALUE=Back ONCLICK="window.history.back()"></FORM> </BODY></HTML> ''' cursor.execute(''' CREATE TABLE users ( login VARCHAR(8), uid INTEGER, prid INTEGER) ''')
Python 替你管理内存,每次你修改一个字符串或者作一些改变字符串内容的操做时,Python 都会自动为你分配一个新串.在下面的例子里面,Python 分别为"abc"和"def"分配了空间,当进行链接操做时,Python 自动为新的字符串"abcdef"分配了空间.
>>> 'abc' + 'def' 'abcdef' 给变量赋值是没什么不一样: >>> s = 'abc' >>> s = s + 'def' >>> s 'abcdef'
上面的例子里,看起来是咱们先把"abc"赋给了s,而后在s 的末尾添加了"def".这样看起来字符串彷佛是可变的,其实事实是在"s+'def""这个操做进行的时候,新建了一个新字符串,而后这个新的对象被赋给了s,原来的字符串'abc'被析构掉了.
咱们能够用id()函数来更明显的显示出来到底发生了什么:
>> s = 'abc' >>> id(s) 135060856 >>> s += 'def' >>> id(s) 135057968
注意修改先后的身份是不一样的.另外一个测试是针对字符串的一个字符或者一个子串所作的修改.咱们如今将展现对字符串的一个字符或者一片字符的改动都是不被容许的:
>>> s 'abcdef' >>> s[2] = 'C' Traceback (innermost last): File "<stdin>", line 1, in ? AttributeError: __setitem__ >>> s[3:6] = 'DEF' Traceback (innermost last): File "<stdin>", line 1, in ? AttributeError: __setslice__
两个操做都抛出了异常.为了实现要求,咱们须要用现有字符串的子串来构建一个新串,而后把这个新串赋给原来的变量:
>>> s 'abcdef' >>> >>> s = '%sC%s' % (s[0:2], s[3:]) >>> s 'abCdef' >>> s[0:3] + 'DEF' 'abCDEF'
对像字符串这样的不可变对象,左值必须是一个完整的对象,好比说一个字符串对象,不能是字符串的一部分.对赋值操做的右值没有这个限制.
从Python1.6 起引进的Unicode 字符串支持,是用来在多种双字节字符的格式、编码进行转换的,其中包括一些对这类字符串的操做管理功能。内建的字符串和正则表达式对Unicode 字符串的支持,再加上string 模块的辅助,Python 已经能够应付大部分应用对Unicode 的存储、访问、操做的须要了。
Unicode 是计算机能够支持这个星球上多种语言的秘密武器.在Unicode 以前,用的都是ASCII,ASCII 码很是简单,每一个英文字符都是以七位二进制数的方式存贮在计算机内,其范围是32 到126.当用户在文件中键入一个大写字符A 时,计算机会把A 的ASCII 码值65写入磁盘,而后当计算机读取该文件时,它会首先把65 转化成字符A 而后显示到屏幕上.ASCII 编码的文件小巧易读。一个程序只需简单地把文件的每一个字节读出来,把对应的数值转换成字符显示出来就能够了.可是ASCII 字符只能表示95 个可打印字符.后来的软件厂商把ASCII 码扩展到了8 位,这样一来它就能够多标识128 个字符,但是223 个字符对须要成千上万的字符的非欧洲语系的语言来讲仍然太少。Unicode 经过使用一个或多个字节来表示一个字符的方法突破了ASCII 的限制. 在这样机制下, Unicode 能够表示超过90,000 个字符.
早先,Python 只能处理8 位的ASCII 值,字符串就是简单的数据类型,为了处理一个字符串,用户必须首先建立一个字符串,而后把它做为参数传给string 模块的一个函数来处理.
2000年,Python1.6(和2.0)版释出,Unicode 第一次在Python 里面获得了支持.
为了让Unicode 和ASCII 码值的字符串看起来尽量的相像,Python 的字符串从原来的简单数据类型改为了真正的对象.ASCII 字符串成了StringType,而Unicode 字符串成了UnicodeType 类型.它们的行为是很是相近的.string 模块里面都有相应的处理函数.string 模块已经中止了更新,只保留了ASCII 码的支持,string 模块已经不推荐使用,在任何须要跟Unicode 兼容的代码里都不要再用该模块,Python 保留该模块仅仅是为了向后兼容。
Python 里面处理Unicode 字符串跟处理ASCII 字符串没什么两样.Python 把硬编码的字符串叫作字面上的字符串,默认全部字面上的字符串都用ASCII 编码,能够经过在字符串前面加一个'u'前缀的方式声明Unicode 字符串,这个'u'前缀告诉Python 后面的字符串要编码成Unicode字符串 .
>>> "Hello World" # ASCII string >>> u"Hello World" # Unicode string
内建的str()函数和chr()函数并无升级成能够处理Unicode.它们只能处理常规的ASCII 编码字符串,若是一个Unicode 字符串被做做为参数传给了str()函数,它会首先被转换成ASCII 字符串而后在交给str()函数.若是该Unicode 字符串中包含任何不被ASCII 字符串支持的字符,会致使str()函数报异常.一样地,chr()函数只能以0 到255 做为参数工做.若是你传给它一个超出此范围的值(好比说一个Unicode 字符),它会报异常.新的内建函数unicode()和unichar()能够当作Unicode 版本的str()和chr().Unicode()函数能够把任何Python 的数据类型转换成一个Unicode 字符串,若是是对象,而且该对象定义了__unicode__()方法,它还能够把该对象转换成相应的Unicode 字符串.具体内容见6.1.3 和6.5.3 章节.
codec 是COder/DECoder 的首字母组合.它定义了文本跟二进制值的转换方式,跟ASCII 那种用一个字节把字符转换成数字的方式不一样,Unicode 用的是多字节.这致使了Unicode 支持多种不一样的编码方式. 好比说codec 支持的四种耳熟能详的编码方式是 :ASCII,ISO8859-1/Latin-1,UTF-8 和UTF-16.中最著名的是UTF-8 编码,它也用一个字节来编码ASCII 字符,这让那些必须同时处理ASCII码和Unicode 码文本的程序员的工做变得很是轻松,由于ASCII 字符的UTF-8 编码跟ASCII 编码彻底相同。
UTF-8 编码能够用1 个到4 个字节来表示其余语言的字符,CJK/East 这样的东亚文字通常都是用3 个字节来表示,那些少用的、特殊的、或者历史遗留的字符用4 个字节来表示.这给那些须要直接处理Unicode 数据的程序员带来了麻烦,由于他们没有办法按照固定长度逐一读出各个字符.幸运的是咱们不须要掌握直接读写Unicode 数据的方法,Python 已经替咱们完成了相关细节,咱们无须为处理多字节字符的复杂问ti而担忧.Python 里面的其余编码不是很经常使用,事实上,咱们认为大部分的Python 程序员根本就用不着去处理其余的编码,UTF-16 多是个例外.
UTF-16 多是之后大行其道的一种编码格式,它容易读写,由于它把全部的字符都是用单独的一个16 位字,两个字节来存储的,正由于此,这两个字节的顺序须要定义一下,通常的UTF-16 编码文件都须要一个BOM(Byte Order Mark),或者你显式地定义UTF-16-LE(小端)或者UTF-16-BE(大端)字节序.
从技术上讲,UTF-16 也是一种变长编码,但它不是很经常使用(人们通常不会知道或者根本不在乎除了基本多文种平面BMP 以外到底使用的是那种平面),尽管如此,UTF-16 并不向后兼容ASCII,所以,实现它的程序不多,由于你们须要对ASCII 进行支持。
Unicode 支持多种编码格式,这为程序员带来了额外的负担,每当你向一个文件写入字符串的时候,你必须定义一个编码(encoding 参数)用于把对应的Unicode内容转换成你定义的格式,Python 经过Unicode 字符串的encode()函数解决了这个问ti,该函数接受字符串中的字符为参数,输出你指定的编码格式的内容。
因此,每次咱们写一个Unicode 字符串到磁盘上咱们都要用指定的编码器给他"编码"一下,相应地,当咱们从这个文件读取数据时,咱们必须"解码"该文件,使之成为相应的Unicode 字符串对象.
简单的例子(uniFile.py):下面的代码建立了一个Unicode 字符串,用UTF-8 编码器将它编码,而后写入到一个文件中去.接着把数据从文件中读回来,解码成Unicode 字符串对象.最后,打印出Unicode 字符串,用以确认程序正确地运行.
CODEC = 'utf-8' FILE = 'unicode.txt' hello_out = u"Hello world\n" #建立了一个Unicode 字符串 bytes_out = hello_out.encode(CODEC) #指定的编码格式对其进行编码 f = open(FILE, 'w') f.write(bytes_out) f.close() f = open(FILE, 'r') bytes_in = f.read() f.close() hello_in = bytes_in.decode(CODEC) # 解码 print (hello_in,)
运行
$ unicode_example.py Hello World
在文件系统中也会发现一个叫unicode.txt 的文件,里面包含跟输出的内容一致的数据.
$ cat unicode.txt Hello World!
简单Web 例子:在第20 章Web 编程里面咱们展现了一个简单的在CGI 应用中使用Unicode 的例子.
这些处理Unicode 字符串的例子简单到让人感到有点假,事实上,只要你遵照如下的规则,处理Unicode 就是这么简单:
程序中出现字符串时必定要加个前缀 u.
不要用 str()函数,用unicode()代替.
不要用过期的 string 模块 -- 若是传给它的是非ASCII 字符,它会把一切搞砸。
不到必须时不要在你的程序里面编解码 Unicod 字符.只在你要写入文件或数据库或者网络时,才调用encode()函数;相应地,只在你须要把数据读回来的时候才调用decode()函数.
这些规则能够规避90%因为Unicode 字符串处理引发的bug.可是剩下的10%的问ti却让你处理不了,幸好Python 提供了大量的模块、库来替你处理这些问ti.它们可让你用10 行Python 语句写出其余语言须要100 行语句才能完成的功能,可是相应地,对Unicode 支持的质量也彻底取决于这些模块、库.Python 标准库里面的绝大部分模块都是兼容Unicode 的.除了pickle 模块!pickle 模块只支持ASCII 字符串。若是你把一个Unicode 字符串交给pickle 模块来unpickle,它会报异常.
你必须先把你的字符串转换成ASCII 字符串才能够.因此最好是避免基于文本的pickle 操做.幸运地是如今二进制格式已经做为pickle 的默认格式了,pickle 的二进制格式支持不错.这点在你向数据库里面存东西是尤其突出,把它们做为BLOB 字段存储而不是做为TEXT 或者VARCHAR字段存储要好不少.万一有人把你的字段改为了Unicode 类型,这能够避免pickle 的崩溃.
若是你的程序里面用到了不少第三方模块,那么你极可能在各个模块统一使用Unicode 通信方面遇到麻烦,Unicode 还没成为一项必须的规定,在你系统里面的第三方模块(包括你的应用要面对的平台\系统)须要用相同的Unicode 编码,不然,可能你就不能正确的读写数据.
做为一个例子,假设你正在构建一个用数据库来读写Unicode 数据的Web 应用.为了支持Unicode,你必须确保如下方面对Unicode 的支持:
数据库服务器(MySQL,PostgreSQL,SQL Server,等等)
数据库适配器(MySQLdb 等等)
Web 开发框架(mod_python,cgi,Zope,Plane,Django 等等)
数据库方面最容易对付,你只要确保每张表都用UTF-8 编码就能够了。
数据库适配器可能有点麻烦,有些适配器支持Unicode 有些不支持,好比说MySQLdb,它并非默认就支持Unicode 模式,你必须在connect()方法里面用一个特殊的关键字use_unicode来确保你获得的查询结果是Unicode 字符串.
mod_python 里面开启对Unicode 的支持至关简单, 只要在request 对象里面把text-encoding 一项设成"utf-8"就好了,剩下的mod_python 都会替你完成,Zope 等其余复杂的系统可能须要更多的工做来支持Unicode.
失误 #1: 你必须在一个极有限的时间内写出一个大型的应用,并且须要其余语言的支持,可是产品经理并无明肯定义这一点。你并无kao虑Unicode 的兼容,直到项目快要结束... ,这时候再添加Unicode 的支持几乎不太可能,不是吗?
结果 #1: 没能预测到最终用户对其余语言界面的需求,在集成他们用的面向其余语种的应用时又没有使用Unicode 支持.更新整个系统既让让人以为枯燥和更是浪费时间。
失误 #2:在源码中处处使用string 模块或者str()和chr()函数.
结果 #2:经过全局的查找替换把str()和chr()替换成unicode()和unichr(),可是这样一来极可能就不能再用pickle 模块,要用只能把全部要pickle 处理的数据存成二进制形式,这样一来就必须修改数据库的结构,而修改数据库结构就意味着所有推倒重来.
失误 #3: 不能肯定全部的辅助系统都彻底地支持Unicode.
结果 #3: 不得不去为那些系统打补丁,而其中有些系统可能你根本就没有源码.修复对Unicode 支持的bug 可能会下降代码的可靠性,并且很是有可能引入新的bug.
总结: 使应用程序彻底支持Unicode,兼容其余的语言自己就是一个工程.
它须要详细的kao虑、计划.全部涉及到的软件、系统都须要检查,包括Python 的标准库和其余将要用到的第三方扩展模块.你甚至有可能须要组建一个经验丰富的团队来专门负责国际化(I18N)问ti。
内建的unicode()函数
Unicode 的工厂方法,同Unicode 字符串操做符(u / U)的工做方式很相似,它接受一个string 作参数,返回一个Unicode 字符串.
内建的decode()/encode()方法
decode()和encode()内建函数接受一个字符串作参数返回该字符串对应的解码后/编码后的字符串.decode()和encode()均可以应用于常规字符串和Unicode 字符串.decode()方法是在Python2.2 之后加入的.
Unicode 类型
Unicode 字符串对象是basestring 的子类、用Unicode()工厂方法或直接在字符串前面加一个u 或者U 来建立实例.支持Unicode 原始字符串,只要在你的字符串前面加一个ur 或者UR就能够了.
Unicode 序数
标准内建函数ord()工做方式相同,最近已经升级到能够支持Unicode 对象了。内建的unichr()函数返回一个对应的Unicode 字符(须要一个32 位的值);不然就产生一个ValueError异常.
强制类型转换
混合类型字符串操做须要把普通字符串转换成Unicode 对象.
异常
UnicodeError 异常是在exceptions 模块中定义的,ValueError 的子类.全部关于Unicode编解码的异常都要继承自UnicodeError.详见encode()函数.
标准编码
表6.9 简洁地列出了Python 中经常使用的编码方式.更详细、彻底的列表见Python 的文档,下面是它的连接:
http://docs.python.org/lib/standard-encodings.html
RE 引擎对Unicode 的支持
正则表达式引擎须要Unicode 支持.详见6.9 节的re 模块.
表6.9 经常使用Unicode 编辑码
编码 描述
utf-8 变量长度为8 的编码(默认编码)
utf-16 变量长度为16 的编码(大/小端)
utf-16-le 小端UTF-16 编码
utf-16-be 大端UTF-16 编码
ascii 7-bit 7 位ASCII 码表
iso-8859-1 ISO 8859-1 (Latin-1) 码表
unicode-escape (定义见Python Unicode 构造函数)
raw-unicode-escape (定义见Python Unicode 构造函数)
native Python 用的内部格式
字符串格式化操做符
对于Python 的格式化字符串的操做符,%s 把Python 字符串中的Unicode 对象执行了str(u)操做,因此,输出的应该是u.encode(默认编码).若是格式化字符串是Unicode 对象,全部的参数都将首先强制转换成Unicode 而后根据对应的格式串一块儿进行格式转换.数字首先被转换成普通字符串, 而后在转换成Unicode.Python 字符串经过默认编码格式转化成Unicode.Unicode 对象不变,全部其余格式字符串都须要像上面这样转化,下面是例子:
u"%s %s" % (u"abc", "abc") u"abc abc"
6.9 相关模块
表6.10 列出了Python 标准库里面与字符串有关的主要模块.
核心模块: re
正则表达式(RE)提供了高级的字符串模式匹配方法.经过描述这些模式的语法,你能够像使用“过滤器”同样高效地查找传进来的文本。这些过滤器容许你基于自定义的模式字符串抽取匹配模式、执行查找-替换或分割字符串.
re全面采用了Perl 正则表达式语法,使得Python 在对正则表达式的支持方面前进了一大步. Python1.6 里面重写了正则表达式引擎(SRE),增长了对Unicode 字符串的支持并对性能进行了重大的升级.SRE 引擎取代了原有正则表达式的模块下的PCRE 引擎.
该模块中包含的关键函数有:
compile() - 将一个RE 表达式编译成一个可重用的RE 对象;
match() - 试图从字符串的开始匹配一个模式;
search() - 找出字符串中全部匹配的项;
sub() - 进行查找替换操做。其中的一些函数返回匹配到的对象,你能够经过组匹配来访问(若是找到的话)。
15 章的整章内容都是讲述正则表达式。
一些引号分隔的字符
你能够把字符串当作是Python 的一种数据类型,在Python 单引号或者双引号之间的字符数组或者是连续的字符集合.字符串的实际内容是这些单引号(')或者双引号(")之间的字符,不包括引号自己.
能够用两种引号来建立字符串是颇有益处的,由于是当你的字符串中包含单引号时,若是用单引号建立字符串,那么字符串中的双引号就不须要转义。反之亦然.
不可分字符类型
字符串是惟一的字面上的字符序列类型.不过,字符自己并非一种类型,因此,字符串是字符存储操做的最基本单位.字符应该视为长度为1 的字符串.
字符串格式化操做符 ( % )提供相似于printf()那样的功能.
字符串格式化操做符(见6.4.1 节)提供了一种基于多种输入类型的建立自定义字符串的灵活方式.它也提供了相似于C/C++世界里的格式化操做的接口.
三引号
在三引号字符串中能够包含诸如换行回车或者tab 键这样的特殊字符.三引号字符串是用两边各三个单引号(''')或者两边各三个双引号(""")来定义的.
原始字符串对每一个特殊字符串都使用它的原意
原始字符串并不经过反斜线转义特殊字符的特性.这个特性使得原始字符串很是适用于那些须要字符串原意的场合,好比在定义一个正则表达式时.
Python 字符串不是经过NUL 或者'\0'来结束的
C 编程的一个主要问ti是你访问了一个字符串后面的本不属于你的空间,这种状况发生在你没有在字符串末尾添加终结符,NUL 或者'\0'(ASCII 值为0)的时候.Python 不只为你自动管理内存,并且也把C 的这个负担或者说是小麻烦去掉了.Python 中的字符串不是以NUL 结束的,因此你不须要为是否已经添加终结符担忧.字符串中只包含你所定义的东西,没有别的.
像字符串类型同样,列表类型也是序列式的数据类型,能够经过下标或者切片操做来访问某一个或者某一块连续的元素.然而,相同的方面也就这些,字符串只能由字符组成,并且是不可变的(不能单独改变它的某个值),而列表则是能保留任意数目的Python 对象的灵活的容器。
列表不只能够包含Python 的标准类型,并且能够用用户定义的对象做为本身的元素.列表能够包含不一样类型的对象,并且要比C 或者Python 本身的数组类型(包含在array 扩展包中)都要灵活。列表能够执行pop,empt,sort,reverse 等操做.列表也能够添加或者减小元素.还能够跟其余的列表结合或者把一个列表分红几个.能够对单独一个元素或者多个元素执行insert,update,或者remove 操做.
元组类型在不少操做上都跟列表同样,许多用在列表上的例子在元组上照样能跑,它们的主要不一样在于元组是不可变的,或者说是只读的,因此那些用于更新列表的操做,好比用切片操做来更新一部分元素的操做,就不能用于元组类型.
如何建立列表类型数据并给它赋值
建立一个列表就像给一个变量赋值同样的简单.你手工写一个列表(空的或者有值的都行)而后赋给一个变量,列表是由方括号([])来定义的,固然,你也能够用工厂方法来建立它.
>>> aList = [123, 'abc', 4.56, ['inner', 'list'], 7-9j] >>> anotherList = [None, 'something to see here'] >>> print aList [123, 'abc', 4.56, ['inner', 'list'], (7-9j)] >>> print anotherList [None, 'something to see here'] >>> aListThatStartedEmpty = [] >>> print aListThatStartedEmpty [] >>> list('foo') ['f', 'o', 'o']
如何访问列表中的值
列表的切片操做就像字符串中同样;切片操做符([])和索引值或索引值范围一块儿使用
如何更新列表
你能够经过在等号的左边指定一个索引或者索引范围的方式来更新一个或几个元素,你也能够用append()方法来追加元素到列表中去.
>>> aList [123, 'abc', 4.56, ['inner', 'list'], (7-9j)] >>> aList[2] = 'float replacer' >>> aList [123, 'abc', 'float replacer', ['inner', 'list'], (7-9j)] >>> anotherList.append("hi, i'm new here") >>> print anotherList [None, 'something to see here', "hi, i'm new here"] >>> aListThatStartedEmpty.append('not empty anymore') >>> print aListThatStartedEmpty ['not empty anymore']
如何删除列表中的元素或者列表(自己)
要删除列表中的元素,若是你知道要删除元素的素引能够用del 语句,不然能够用remove()方法.
>>> aList [123, 'abc', 'float replacer', ['inner', 'list'], (7-9j)] >>> del aList[1] >>> aList [123, 'float replacer', ['inner', 'list'], (7-9j)] >>> aList.remove(123) >>> aList ['float replacer', ['inner', 'list'], (7-9j)]
你还能够经过pop()方法来删除并从列表中返回一个特定对象.
通常来讲,程序员不须要去删除一个列表对象。列表对象出了做用域(好比程序结束,函数调用完成等等)后它会自动被析构,可是若是你想明确的删除一整个列表,你能够用del 语句:
del aList
在第4 章里,咱们介绍了一些适用于包括标准类型在内的大部分对象的操做符,如今咱们来看一下这些操做符如何做用在列表上:
>>> list1 = ['abc', 123] >>> list2 = ['xyz', 789] >>> list3 = ['abc', 123] >>> 1ist1 < list2 True >>> list2 < list3 False >>> list2 > list3 and list1 == list3 True
比较运算符用在列表上时就不是那么简单了。比较列表时也是用的内建的cmp()函数,基本的比较逻辑是这样的:两个列表的元素分别比较,直到有一方的元素胜出,
好比咱们上面的例子,'abc'和'xyz'的比较直接决定了比较结果,在'abc'<'xyz'时,list1<list2,list2>=list3,元组类型在进行比较操做时跟列表遵循相同的逻辑.
切片([] 和[:])
列表的切片操做跟字符串的切片操做很像,不过列表的切片操做返回的是一个对象或者是几个对象的集合,而不是像字符串那样,返回一个字符或者一个子串.咱们定义如下几个列表用来作例子:
>>> num_list = [43, -1.23, -2, 6.19e5] >>> str_list = ['jack', 'jumped', 'over', 'candlestick'] >>> mixup_list = [4.0, [1, 'x'], 'beef', -1.9+6j]
列表的切片操做也听从正负索引规则,也有开始索引值,结束索引值,若是这两个值为空,默认也会分别指到序列的开始和结束位置.
>>> num_list[1] -1.23 >>> num_list[1:] [-1.23, -2, 619000.0] >>> num_list[2:-1] [-2] >>> >>> str_list[2] 'over' >>> str_list[:2] ['jack', 'jumped'] >>> mixup_list [4.0, [1, 'x'], 'beef', (-1.9+6j)] >>> mixup_list[1] [1, 'x']
跟字符串类型只能用字符为元素不一样,列表类型的元素能够是另外一个序列类型,意味着在列表的元素上也可使用全部的序列操做符或者在其之上执行序列类型内建的各类操做.在下面的例子中,咱们将会展现,不只能够在一个切片操做的结果之上再进行切片,并且还能够改变这个切片的结果,即便新对象的类型跟原对象不一样也能够.这跟多维数组有一些相似.
>>> mixup_list[1][1] 'x' >>> mixup_list[1][1] = -64.875 >>> mixup_list [4.0, [1, -64.875], 'beef', (-1.9+6j)] 这时用num_list 来作的另外一个例子: >>> num_list [43, -1.23, -2, 6.19e5] >>> num_list[2:4] = [16.0, -49] >>> num_list [43, -1.23, 16.0, -49] >>> num_list[0] = [65535L, 2e30, 76.45-1.3j] >>> num_list [[65535L, 2e+30, (76.45-1.3j)], -1.23, 16.0, -49]
注意在最后一个例子中,是如何把列表的单一元素替换成一个列表.在列表中进行诸如remove,add,和replace 的操做是多么的自由了吧!还有一点要注意,若是你想以子列表的形式获得一个列表中的一个切片,那须要确保在赋值时等号的左边也是一个列表而不是一个列表的元素.
成员关系操做( in ,not in)
列表中(一样适用于元组),咱们能够检查一个对象是不是一个列表(或者元组)的成员.
>>> mixup_list [4.0, [1, 'x'], 'beef', (-1.9+6j)] >>> 'beef' in mixup_list True >>> 'x' in mixup_list False >>> 'x' in mixup_list[1] True >>> num_list [[65535L, 2e+030, (76.45-1.3j)], -1.23, 16.0, -49] >>> -49 in num_list True >>> 34 in num_list False >>> [65535L, 2e+030, (76.45-1.3j)] in num_list True
链接接操做符( + )
链接操做符容许把多个列表对象合并在一块儿.注意,列表类型的链接操做也只能在同类型之间进行,换句话说,你不能把两个不一样类型的对象(好比:[1,2]+'a')链接在一块儿,即使他们都是序列类型也不行.
>>> num_list = [43, -1.23, -2, 6.19e5] >>> str_list = ['jack', 'jumped', 'over', 'candlestick'] >>> mixup_list = [4.0, [1, 'x'], 'beef', -1.9+6j] >>> num_list + mixup_list [43, -1.23, -2, 619000.0, 4.0, [1, 'x'], 'beef', (-1.9+6j)] >>> str_list + num_list ['jack', 'jumped', 'over', 'candlestick', 43, -1.23, -2, 619000.0]
用extend()方法来代替链接操做符把一个列表的内容添加到另外一个中去.使用extend()方法比链接操做的一个优势是它其实是把新列表添加到了原有的列表里面,而不是像链接操做那样新建一个列表。
必须指出,链接操做符并不能实现向列表中添加新元素的操做.在接下来的例子中,咱们展现了一个试图用链接操做向列表中添加新元素报错的例子.
>>> num_list + 'new item' Traceback (innermost last): File "<stdin>", line 1, in ? TypeError: illegal argument type for built-in operation
错误是由于咱们在链接操做符的左右两边使用了不一样类型的值.显然,咱们的初衷是把一个字符串做为一个新元素添加到列表中去,不过方法不正确.咱们有一个正确的方法:
使用内建函数append() (咱们会在6.13 节里面正是地介绍append()和其余内建函数)
>>> num_list.append('new item')
重复操做符( * )
重复操做符可能更多的应用在字符串类型中,不过,列表和元组跟字符串同属序列类型,因此须要的时候也可使用这一操做.
>>> num_list * 2 [43, -1.23, -2, 619000.0, 43, -1.23, -2, 619000.0] >>> >>> num_list * 3 [43, -1.23, -2, 619000.0, 43, -1.23, -2, 619000.0, 43, -1.23, -2, 619000.0]
Python2.0 起,也开始支持复合赋值运算:
>>> hr = '-' >>> hr *= 30 >>> hr '------------------------------'
其实Python 中没有专门用于列表类型的操做符.列表可使用大部分的对象和序列类型的操做符.此外,列表类型有属于本身的方法.列表才有的构建--列表解析.这种方法是结合了列表的方括弧和for 循环,在逻辑上描述要建立的列表的内容.咱们在第八章讨论列表解析,这里仅仅向本章其余地方所作的那样,展现一个简单的例子:
>>> [ i * 2 for i in [8, -2, 5] ] [16, -4, 10] >>> [ i for i in range(8) if i % 2 == 0 ] [0, 2, 4, 6]
cmp()
咱们还不知道cmp()函数是如何跟其余的好比列表和元组类型合做的,这些类型不只含有数字和字符串,并且还有列表,元组,字典之类的其余对象,甚至能够是用户自定义的对象.这种状况下cmp()函数是如何工做的呢?
>>> list1, list2 = [123, 'xyz'], [456, 'abc'] >>> cmp(list1, list2) -1 >>> cmp(list2, list1) 1 >>> list3 = list2 + [789] >>> list3 [456, 'abc', 789] >>> cmp(list2, list3) -1
对于序列类型,比较操做稍微有点复杂了,好比当两个对象没有关系时或者两种类型根本就没有用于比较的函数时,这时Python 只能根据"逻辑"来作出结论.
除了这种极端的状况以外,安全又健全的比较方法是:若是有不相等的状况出现,比较操做就结束.这种算法是如何工做的呢?
列表的元素是能够无限迭代的.若是它的元素都是相同类型,则用标准的比较方法来做比较.不然,若是要比较的元素类型不一致,那么要获得一个准确的或者说绝对的比较结果就有些冒险.
当咱们较list1 和list2 时,list1 和list2 进行逐项比较.第一个比较操做发生在两个列表的第一个元素之间,好比说,123 跟456 比较,由于123<456,因此list1 被认为小于list2.
若是比较的值相等,那么两个序列的下一个值继续比较,直到不相等的状况出现,或者到达较短的一个序列的末尾,在这种状况下,长的序列被认为是"较大"的.这就是为何上面的list2<list3 的缘由.元组类型比较也是用这种算法.最后咱们以这种算法的关键点做为本节的结束:
1. 对两个列表的元素进行比较.
2. 若是比较的元素是同类型的,则比较其值,返回结果.
3. 若是两个元素不是同一种类型,则检查它们是不是数字.
a. 若是是数字,执行必要的数字强制类型转换,而后比较.
b. 若是有一方的元素是数字,则另外一方的元素"大"(数字是"最小的")
c. 不然,经过类型名字的字母顺序进行比较.
4. 若是有一个列表首先到达末尾,则另外一个长一点的列表"大".
5. 若是咱们用尽了两个列表的元素并且全部元素都是相等的,那么结果就是个平局,就是说返回一个0.
len()
对列表或者元组来讲,返回列表或者元组的元素个数,容器里面的每一个对象被做为一个项来处理:
>>> len(num_list) 4 >>> len(num_list*2) 8
max()和min()
max()和min()函数对列表和元组来讲,它们被定义了不少的用处.好比对只包含数字和字符串对象的列表,max()和min()函数就很是有用,混合对象的结构越复杂返回的结构准确性就越差:
>>> max(str_list) 'park' >>> max(num_list) [65535L, 2e+30, (76.45-1.3j)] >>> min(str_list) 'candlestick' >>> min(num_list) -49
sorted() and reversed()
>>> s = ['They', 'stamp', 'them', 'when', "they're", 'small'] >>> for t in reversed(s): ... print t, ... small they're when them stamp They >>> sorted(s) ['They', 'small', 'stamp', 'them', "they're", 'when']
注意字符串排序使用的是字典序,而不是字母序(字母'T'的ASCII 码值要比字母'a'的还要靠前)
enumerate() and zip()
>>> albums = ['tales', 'robot', 'pyramid'] >>> for i, album in enumerate(albums): ... print i, album ... 0 tales 1 robot 2 pyramid >>> fn = ['ian', 'stuart', 'david'] >>> ln = ['bairnson', 'elliott', 'paton'] >>> for i, j in zip(fn, ln): ... print ('%s %s' % (i,j)).title() ... Ian Bairnson Stuart Elliott David Paton
sum()
>>> a = [6, 4, 5] >>> reduce(operator.add, a) 15 >>> sum(a) 15 >>> sum(a, 5) 20 >>> a = [6., 4., 5.] >>> sum(a) 15.0
list() and tuple()
list()函数和tuple()函数接受可迭代对象(好比另外一个序列)做为参数,并经过浅拷贝数据来建立一个新的列表或者元组.虽然字符串也是序列类型的,可是它们并非常常用于list()和tuple(). 更多的状况下,它们用于在两种类型之间进行转换,好比你须要把一个已有的元组转成列表类型的(而后你就能够修改它的元素了),或者相反.
>>> aList = ['tao', 93, 99, 'time'] >>> aTuple = tuple(aList) >>> aList, aTuple (['tao', 93, 99, 'time'], ('tao', 93, 99, 'time')) >>> aList == aTuple False >>> anotherList = list(aTuple) >>> aList == anotherList True >>> aList is anotherList False >>> [id(x) for x in aList, aTuple, anotherList] [10903800, 11794448, 11721544]
正如咱们在本章的开头所讨论的,不管list()仍是tuple()都不可能作彻底的转换(见6.1.2 节).也就是说,传给tuple()的一个列表对象不可能变成一个元组,而你传给list()的对象也不可能真正的变成一个列表.虽然先后两个对象(原来的和新的对象)有着相同的数据集合(因此相等 == ),可是变量指向的却不是同一个对象了(因此执行 is 操做会返回false).还要注意,即便它们的全部的值都相同,一个列表也不可能"等于"一个元组.
若是你不考虑range()函数的话,Python 中没有特定用于列表的内建函数.range()函数接受一个数值做为输入,输出一个符合标准的列表.第8 章里面详细讨论了range()函数.列表类型对象可使用大多数的对象和序列的内建函数,而且,列表对象有属于它们本身的方法.
Python 中的列表类型有本身的方法.咱们会在第13 章面向对象编程里面正式而详细的介绍方法这一律念,如今你只须要把方法视为特定对象的函数或者过程就好.本节讨论的方法就像内建的函数同样,除了它们只对列表类型进行操做以外.由于这些函数涉及到对列表更改(或者说更新),因此它们都不适应于元组.
列表的方法也是这样:list.method().咱们用点号来访问一个对象的属性,而后用函数操做符( () )来调用这个方法.
能够在一个列表对象上应用dir()方法来获得它全部的方法和属性:
>>> dir(list) # or dir([]) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
表6.11 列出了目前列表类型支持的全部方法,稍后咱们给出使用这些方法的例子.
>>> music_media = [45] >>> music_media [45] >>> music_media.insert(0, 'compact disc') >>> music_media ['compact disc', 45] >>> music_media.append('long playing record') >>> music_media ['compact disc', 45, 'long playing record'] >>> music_media.insert(2, '8-track tape') >>> music_media ['compact disc', 45, '8-track tape', 'long playing record']
在这个例子中,向列表插入元素,或在尾部追加新的元素后,都会去检查这个列表.如今确认一下一个值是否在咱们的列表中,并看看如何找出元素在列表中的索引值.咱们用in 操做符和index()方法实现这两个需求.
>>> 'cassette' in music_media False >>> 'compact disc' in music_media True >>> music_media.index(45) 1 >>> music_media.index('8-track tape') 2 >>> music_media.index('cassette') Traceback (innermost last): File "<interactive input>", line 0, in ? ValueError: list.index(x): x not in list
看起来用index()来检查一个元素是否存在于一个list中并非个好主意,由于咱们出错了.应该先用in 成员关系操做符(或者是not in)检查一下,而后在用index()找到这个元素的位置:
for eachMediaType in (45, '8-track tape', 'cassette'): if eachMediaType in music_media: print music_media.index(eachMediaType)
稍后咱们将会发现该如何处理这种错误,而不是这样的一出错,程序就崩溃了。
接下来测试sort()和reverse()方法,它们会把列表中的元素排序,而后翻转.
>>> music_media ['compact disc', 45, '8-track tape', 'long playing record'] >>> music_media.sort() >>> music_media [45, '8-track tape', 'compact disc', 'long playing record'] >>> music_media.reverse() >>> music_media ['long playing record', 'compact disc', '8-track tape', 45]
核心笔记:那些能够改变对象值的可变对象的方法是没有返回值的
Python 初学者常常会陷入一个误区:调用一个方法就返回一个值.最明显的例子就是sort():
>>> music_media.sort()# 没有输出? >>>
在使用可变对象的方法如sort(),extend()和reverse()的时候要注意,这些操做会在列表中原地执行操做,也就是说现有的列表内容会被改变,可是没有返回值!是的,与之相反,字符串方法确实有返回值:
>>> 'leanna, silly girl!'.upper() 'LEANNA, SILLY GIRL!'
温习一下,字符串是不可变的 -- 不可变对象的方法是不能改变它们的值的,因此它们必须返回一个新的对象.若是你确实须要返回一个对象,那么咱们建议你看一下Python2.4 之后加入的reversed()和sorted()内建函数.
它们像列表的方法同样工做,不一样的是它们能够用作表达式,由于它们返回一个对象.同时原来的那个列表仍是那个列表,没有改变,而你获得的是一个新的对象.
回到sort()方法,它默认的排序算法是归并排序的衍生算法,时间复杂度是O(lg(n!)).能够经过源码查看它们的详情 --Objects/listobject.c,还有算法描述: Objects/listsort.txt.
extend()方法接受一个列表的内容而后把它的全部元素追加到另外一个列表中去:
>>> new_media = ['24/96 digital audio disc', 'DVD Audio disc', 'Super Audio CD'] >>> music_media.extend(new_media) >>> music_media ['long playing record', 'compact disc', '8-track tape', 45, '24/96 digital audio disc', 'DVD Audio disc', 'Super Audio CD']
extend()方法的参数支持任何可迭代对象,经过可迭代对象,你能作更多有趣的事情,好比:
>>> motd = [] >>> motd.append('MSG OF THE DAY') >>> f = open('/etc/motd', 'r') >>> motd.extend(f) >>> f.close() >>> motd ['MSG OF THE DAY', 'Welcome to Darwin!\n']
1.5.2 中加入的pop()方法会从列表中把最后的或指定的元素返回调用者.咱们会在6.15.1节和练习中看到pop()方法,
列表有容器和可变的特性,这使得它很是灵活,用它来构建其余的数据结构不是件难事.咱们立刻能想到的是堆栈和队列.
堆栈
堆栈是一个后进先出(LIFO)的数据结构.在栈上"push"元素是个经常使用术语,意思是把一个对象添加到堆栈中。反之,要删除一个元素,你能够把它"pop"出堆栈。
例6.3展现了一个菜单驱动的程序,它实现了一个简单的、用于存储字符串的堆栈.
stack = [] def pushit(): stack.append(input("enter new string: ").strip()) def popit(): if len(stack) == 0: print ('cannot pop from an empty stack!') else: print ('remove[',stack.pop(), ']') def viewstack(): print (stack) CMDS = {'u':pushit, 'o':popit, 'v':viewstack} def showmenu(): pr="""p(U)sh p(O)p (V)iew (Q)uit Enter choice:""" while True: while True: try: choice = input(pr).strip()[0].lower() except(EOFError, KeyboardInterrupt, IndexError): choice='q' print ('your choice',choice) if choice not in 'uovq': print ('invalid option, try again') else: break if choice == 'q': break CMDS[choice]() if __name__ == "__main__": showmenu()
队列
队列是一种先进先出(FIFO)的数据类型,.新的元素经过"入队"的方式添加进队列的末尾,"出队"就是从队列的头部删除.下面的例子里面展现了这种操做,咱们把上面的堆栈的例子进行如下改造就能够用列表实现了一个简单的队列.
print ('remove[',stack.pop(0), ']') #其他都同样,只不过出来的时候出来第一个
元组跟列表很是相近的另外一种容器类型.元组和列表看起来不一样的一点是元组用的是圆括号而列表用的是方括号。而功能上,元组和列表相比有一个很重要的区别,元组是一种不可变类型.正由于这个缘由,元组能作一些列表不能作的事情... 用作一个字典的key.另外当处理一组对象时,这个组默认是元组类型.
因为元组类型跟列表类型有着很是多的共同之处,为了不太多重复信息,咱们会讲解元组和列表在应用于每一组操做符和内建函数上时的区别,而后讨论一下元组的不变性以及其余独特的特性.
如何建立一个元组并给它赋值
建立一个元组并给他赋值实际上跟建立一个列表并给它赋值彻底同样,除了一点,只有一个元素的元组须要在元组分割符里面加一个逗号(,)用以防止跟普通的分组操做符混淆.不要忘了它是一个工厂方法!
>>> aTuple = (123, 'abc', 4.56, ['inner', 'tuple'], 7-9j) >>> anotherTuple = (None, 'something to see here') >>> print aTuple (123, 'abc', 4.56, ['inner', 'tuple'], (7-9j)) >>> print anotherTuple (None, 'something to see here') >>> emptiestPossibleTuple = (None,) >>> print emptiestPossibleTuple (None,) >>> tuple('bar') ('b', 'a', 'r')
如何访问元组中的值
元组的切片操做跟列表同样
如何更新元组
跟数字和字符串同样,元组也是不可变类型,就是说你不能更新或者改变元组的元素,在6.2和6.3.2 节里面,咱们是经过现有字符串的片断再构造一个新字符串的方式解决的,对元组一样须要这样.
>>> aTuple = aTuple[0], aTuple[1], aTuple[-1] >>> aTuple (123, 'abc', (7-9j)) >>> tup1 = (12, 34.56) >>> tup2 = ('abc', 'xyz') >>> tup3 = tup1 + tup2 >>> tup3 (12, 34.56, 'abc', 'xyz')
如何移除一个元组的元素以及元组自己
删除一个单独的元组元素是不可能的,固然,把不须要的元素丢弃后, 从新组成一个元组是ok的.
要显示地删除一整个元组,只要用del 语句减小对象引用计数.当这个引用计数达到0 的时候,该对象就会被析构.记住,大多数时候,咱们不须要显式的用del 删除一个对象,一出它的做用域它就会被析构,Python 编程里面用到显式删除元组的状况很是之少.
del aTuple
元组的对象和序列类型操做符还有内建函数跟列表的彻底同样.你仍然能够对元组进行切片操做,合并操做,以及屡次拷贝一个元组,还能够检查一个对象是否属于一个元组,进行元组之间的比较等.
建立,重复,链接操做
>>> t = (['xyz', 123], 23, -103.4) >>> t (['xyz', 123], 23, -103.4) >>> t * 2 (['xyz', 123], 23, -103.4, ['xyz', 123], 23, -103.4) >>> t = t + ('free', 'easy') >>> t (['xyz', 123], 23, -103.4, 'free', 'easy')
成员关系操做,切片操做
>>> 23 in t True >>> 123 in t False >>> t[0][1] 123 >>> t[1:] (23, -103.4, 'free', 'easy')
内建函数
>>> str(t) (['xyz', 123], 23, -103.4, 'free', 'easy') >>> len(t) 5 >>> max(t) 'free' >>> min(t) -103.4 >>> cmp(t, (['xyz', 123], 23, -103.4, 'free', 'easy')) 0 >>> list(t) [['xyz', 123], 23, -103.4, 'free', 'easy']
操做符
>>> (4, 2) < (3, 5) False >>> (2, 4) < (3, -1) True >>> (2, 4) == (3, -1) False >>> (2, 4) == (2, 4) True
像列表同样 元组也没有它本身专用的运算符和内建函数.上一节中描述的列表方法都跟列表对象的可变性有关,好比说排序,替换,添加等等,由于元组是不可变的,因此这些操做对元组来讲就是多余的,这些方法没有被实现.
在好多地方使用到了"不可变性"这个单词,除了这个词的计算机学科定义和实现,从应用的角度来考虑,这个词的底线是什么?一个数据类型成为不可变的到底意味着什么?
在三个标准不可变类型里面--数字,字符串和元组字符串--元组是受到影响最大的,一个数据类型是不可变的,简单来说,就意味着一旦一个对象被定义了,它的值就不能再被更新,除非从新建立一个新的对象.对数字和字符串的影响不是很大,由于它们是标量类型,当它们表明的值改变时,这种结果是有意义的,是按照你所想要的方式进行访问的,而对于元组,事情就不是这样了。
由于元组是容器对象,不少时候你想改变的只是这个容器中的一个或者多个元素,不幸的是这是不可能的,切片操做符不能用做左值进行赋值。这和字符串没什么不一样,切片操做只能用于只读的操做。
不可变并非坏事,好比咱们把数据传给一个不了解的API 时,能够确保咱们的数据不会被修改。一样地,若是咱们操做从一个函数返回的元组,能够经过内建list()函数把它转换成一个列表.
6.18.2 元组也不是那么“不可变”
虽然元组是被定义成不可变的,但这并不影响它的灵活性。元组并不像咱们想的那么不可变,其实元组几个特定的行为让它看起来并不像咱们先前声称的那么不可变.
好比说,既然咱们能够把字符串组合在一块儿造成一个大字符串。那么把元组组合在一块儿造成一个大的元组也没什么不对,因此,链接操做可用,这个操做一点都没有改变那些小元组:
>>> s = 'first' >>> s = s + ' second' >>> s 'first second' >>> t = ('third', 'fourth') >>> t ('third', 'fourth') >>> t = t + ('fifth', 'sixth') >>> t ('third', 'fourth', 'fifth', 'sixth')
一样的概念也适用于重复操做。重复操做只不过是屡次复制一样的元素,再有,咱们前面提到过能够用一个简单的函数调用把一个元组变成一个可变的列表。咱们的最后一个特性可能会吓到你。你能够“修改”特定的元组元素,哇!这意味着什么?
虽然元组对象自己是不可变的,但这并不意味着元组包含的可变对象也不可变了。
>>> t = (['xyz', 123], 23, -103.4) >>> t (['xyz', 123], 23, -103.4) >>> t[0][1] 123 >>> t[0][1] = ['abc', 'def'] >>> t (['xyz', ['abc', 'def']], 23, -103.4)
在上面的例子中,虽然t 是一个元组类型变量,可是咱们设法经过替换它的第一个元素(一个列表对象)的项来“改变”了它。咱们替换了t[0][1],原来是个整数,咱们把它替换成了一个列表对象 ['abc','def'].虽然咱们只是改变了一个可变对象,但在某种意义上讲,咱们也“改变”了咱们的元组类型变量。
全部的多对象的,逗号分隔的,没有明确用符号定义的,好比说像用方括号表示列表和用圆括号表示元组同样,等等这些集合默认的类型都是元组,下面是一个简单的示例:
>>> 'abc', -4.24e93, 18+6.6j, 'xyz' ('abc', -4.24e+093, (18+6.6j), 'xyz') >>> x, y = 1, 2 >>> x, y (1, 2)
全部函数返回的多对象(不包括有符号封装的)都是元组类型。注意,有符号封装的多对象集合实际上是返回的一个单一的容器对象,好比:
def foo1(): return obj1, obj2, obj3 def foo2(): return [obj1, obj2, obj3] def foo3(): return (obj1, obj2, obj3)
上面的例子中,foo1()返回3 个对象,默认的做为一个包含3 个对象的元组类型,foo2()返回一个单一对象,一个包含3 个对象的列表,还有foo3()返回一个跟foo1()相同的对象.惟一不一样的是这里的元组是显式定义的.
为了不使人讨厌的反作用,建议老是显式的用圆括号表达式表示元组或者建立一个元组.
>>> 4, 2 < 3, 5 # int, comparison, int (4, True, 5) >>> (4, 2) < (3, 5) # tuple comparison False
在第一个例子中小于号的优先级高于逗号,2<3 的结果成了元组变量的第二个元素,适当的封装元组就会获得但愿获得的结果.
曾经试过建立一个只有一个元素的元组?你在列表上试过,它能够完成,可是不管你怎么在元组上试验,你都不能获得想要的结果。
>>> ['abc'] ['abc'] >>> type(['abc']) # a list <type 'list'> >>> ('xyz') 'xyz' >>> type(('xyz')) # a string, not a tuple <type 'str'>
或许你忘记了圆括号被重载了,它也被用做分组操做符。由圆括号包裹的一个单一元素首先被做为分组操做,而不是做为元组的分界符。一个变通的方法是在第一个元素后面添一个逗号(,)来代表这是一个元组而不是在作分组操做.
>>> ('xyz',) ('xyz',)
不可变对象的值是不可改变的。这就意味着它们经过hash 算法获得的值老是一个值。这是做为字典键值的一个必备条件。在下一章节里面咱们会讨论到,键值必须是可哈希的对象,元组变量符合这个标准,而列表变量就不行。
核心笔记:列表 VS 元组
"为何咱们要区分元组和列表变量?",一个缘由是在有些状况下,使用其中的一种类型要优于使用另外一种类型。
最好使用不可变类型变量的一个状况是:你在维护一些敏感的数据,而且须要把这些数据传递给一个并不了解的函数(或许是一个根本不是你写的API),做为一个只负责一个软件某一部分的工程师,若是你确信你的数据不会被调用的函数篡改,你会以为安全了许多。
一个须要可变类型参数的例子是:你在管理动态数据集合时。你须要先把它们建立出来,逐渐地或者不按期的添加它们,或者有时还要移除一些单个的元素。这是一个必须使用可变类型对象的典型例子。幸运的是,经过内建的list()和tuple()转换函数,你能够很是轻松的在二者之间进行转换.
list()和tuple()函数容许你用一个列表来建立一个元组,反之亦然.若是你有一个元组变量,但你须要一个列表变量由于你要更新一下它的对象,这时list()函数就是你最好的帮手.若是你有一个列表变量,而且想把它传递给一个函数,或许一个API,而你又不想让任何人弄乱你的数据,这时tuple()函数就很是有用。
表6.12 列出了与序列类型相关的关键模块,这个列表包含了前面咱们间接提到的数组模块,它就像列表类型,不过它要求全部的元素都是同一类型。
Table 6.12 与序列类型相关的模块
operator 模块除了提供与数字操做符相同的功能外,还提供了与序列类型操做符相同的功能.types 模块是表明python 支持的所有类型的type 对象的引用。最后,UserList 模块包含了list 对象的彻底的类实现。由于Python 类型不能做为子类,因此这个模块容许用户得到相似list 的类,也能够派生出新的类或功能。若是你熟悉面向对象编程的话,咱们强烈推荐你阅读第13 章
浅拷贝和深拷贝
咱们讲过对象赋值其实是简单的对象引用。当你建立一个对象,而后把它赋给另外一个变量的时候,Python 并无拷贝这个对象,而是拷贝了这个对象的引用。
假设你想建立一对小夫妻的通用档an,名为person.而后你分别为他俩拷贝一份。
咱们展现了两种拷贝对象的方式,一种使用了切片操做,另外一种用了工厂方法,为了区分出三个不一样的对象,咱们使用id()内建函数来显示每一个对象的标识符。(咱们还能够用is 操做符来作相同的事情)
>>> person = ['name', ['savings', 100.00]] >>> hubby = person[:] # slice copy >>> wifey = list(person) # fac func copy >>> [id(x) for x in person, hubby, wifey] [11826320, 12223552, 11850936]
为他们建立了初始有$100 的我的存款账户。用户名改成定制的名字。可是,当丈夫取走$50后,他的行为影响到了他妻子的帐户,虽然咱们进行了分开的拷贝,为何会这样呢?
>>> hubby[0] = 'joe' >>> wifey[0] = 'jane' >>> hubby, wifey (['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]]) >>> hubby[1][1] = 50.00 >>> hubby, wifey (['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])
缘由是咱们仅仅作了一个浅拷贝。对一个对象进行浅拷贝实际上是新建立了一个类型跟原对象同样,其内容是原来对象元素的引用,换句话说,这个拷贝的对象自己是新的,可是它的内容不是 .序列类型对象的浅拷贝是默认类型拷贝,并能够如下几种方式实施:
(1)彻底切片操做[:]
(2)利用工厂函数,好比list(),dict()等
(3)使用copy 模块的copy 函数.
但当妻子的名字被赋值,为何丈夫的名字没有受到影响?难道它们的名字如今不该该都是'jane'了吗?这是由于在这两个列表的两个对象中,第一个对象是不可变的(是个字符串类型),而第二个是可变的(一个列表).正由于如此,当进行浅拷贝时,字符串被显式的拷贝,并新建立了一个字符串对象,而列表元素只是把它的引用复制了一下,并非它的成员.因此改变名字没有任何问ti,可是更改他们银行帐号的任何信息都会引起问ti.如今,让咱们分别看一下每一个列表的元素的对象ID 值,注意,银行帐号对象是同一个对象,这也是为何对一个对象进行修改会影响到另外一个的缘由.
BEFORE: >>> [id(x) for x in hubby] [9919616, 11826320] >>> [id(x) for x in wifey] [9919616, 11826320] AFTER: >>> [id(x) for x in hubby] [12092832, 11826320] >>> [id(x) for x in wifey] [12191712, 11826320]
假设咱们要给这对夫妻建立一个联合帐户,那这是一个很是棒的程序,可是,若是须要的是两个分离帐户,就须要做些改动了.要获得一个彻底拷贝或者说深拷贝--建立一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用--须要copy.deepcopy()函数.咱们使用深拷贝来重写整个例子.
>>> person = ['name', ['savings', 100.00]] >>> hubby = person >>> import copy >>> wifey = copy.deepcopy(person) >>> [id(x) for x in person, hubby, wifey] [12242056, 12242056, 12224232] >>> hubby[0] = 'joe' >>> wifey[0] = 'jane' >>> hubby, wifey (['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]]) >>> hubby[1][1] = 50.00 >>> hubby, wifey (['joe', ['savings', 50.0]], ['jane', ['savings', 100.0]])
这就是咱们想要的方式,做为验证,让咱们确认一下全部四个对象都是不一样的.
>>> [id(x) for x in hubby] [12191712, 11826280] >>> [id(x) for x in wifey] [12114080, 12224792]
如下有几点关于拷贝操做的警告。第一,非容器类型(好比数字,字符串和其余"原子"类型的对象,像代码,类型和xrange 对象等)没有被拷贝一说,浅拷贝是用彻底切片操做来完成的.第二,若是元组变量只包含原子类型对象,对它的深拷贝将不会进行.若是咱们把帐户信息改为元组类型,那么即使按咱们的要求使用深拷贝操做也只能获得一个浅拷贝:
>>> person = ['name', ('savings', 100.00)] >>> newPerson = copy.deepcopy(person) >>> [id(x) for x in person, newPerson] [12225352, 12226112] >>> [id(x) for x in person] [9919616, 11800088] >>> [id(x) for x in newPerson] [9919616, 11800088]
核心模块: copy
浅拷贝和深拷贝操做均可以在copy 模块中找到.其实copy 模块中只有两个函数可用:copy()进行浅拷贝操做,而deepcopy()进行深拷贝操做.
序列类型为数据的顺序存储提供了几种机制.字符串是最经常使用的数据载体,不管是用于给用户显示,存贮到硬盘,经过网络传输,仍是做为一个多源信息的容器.列表和元组提供了容器存储能力,容许简单的操做和访问多个对象,不管它们是Python 的对象仍是用户自定义的对象.单一元素或一组元素能够经过持续有序地索引偏移进行切片操做来访问.总之,这些数据类型为你的Python 开发环境提供了灵活而易用的存贮工具.咱们用表6.13--序列类型的操做符,内建函数和方法的摘要列表来总结本章.
Table 6.13 序列类型操做符,内建函数和方法