一、简介
Python序列指:它们的成员是有序排列的,并且可以通过下标偏移量访问到它的一个或者几个成员。包括:字符串(普通字符串和unicode字符串)、列表、和元组类型。
因为这些类型其实都是由一些成员共同组成的一个序列整体,所以把它们统称为序列,比如说一个字符串是由一些字符(尽管Python并没有显式的定义字符这个类型)组成的序列,那么“Hello”这个字符串的第一个字符就是“H",第二个字符就是‘e’......同样的,列表类型和元组类型就是其他一些Python对象所组成的序列。
二、详解
1、序列
序列类型有着相同的访问模式:它的每一个元素可以通过指定一个偏移量的方式得到。而多个元素可以通过切片操作的方式一次得到。下标偏移量是从0开始到总元素数-1 结束 。
(1)标准类型操作符
标准类型操作符一般都能适用于所有的序列类型。当然,如果作复合类型的对象比较的话,这样说可能需要有所保留,不过其他的操作绝对是完全适用的。
(2)序列类型操作符
下如是对所有序列类型都适用的操作符,操作符是按照优先级从高到底的顺序排列的。
成员关系操作符 (in, not in)
成员关系操作符使用来判断一个元素是否属于一个序列的。比如对字符串类型来说,就是判断一个字符是否属于这个字符串,对和元组类型来说,就代表了一个对象是否属于该对象序列。in/not in操作符的返回值一般来讲就是True/False,满足成员关系就返回True,否则返回False。该操作符的语法如下:obj [not] in sequence。
连接操作符( + )
这个操作符允许把一个序列和另一个相同类型的序列做连接。语法如下:sequence1 + sequence2。该表达式的结果是一个包含 sequence1和sequence2的内容的新序列。注意:这种方式看起来似乎实现了把两个序列内容合并的概念,但是这个操作不是最快或者说最有效的。对字符串来说,这个操作不如把所有的子字符串放到一个列表或可迭代对象中,然后调用一个join方法来把所有的内容连接在一起节约内存;类似地,对列表来说,推荐用列表类型的extend()方法来把两个或者多个列表对象合并。当你需要简单地把两个对象的内容合并,或者说不能依赖于可变对象的那些没有返回值(实际上它返回一个 None)的内建方法来完成的时候时,连接操作符还是很方便的一个选择。
重复操作符 ( * )
当需要一个序列的多份拷贝时,重复操作符非常有用,它的语法如下:sequence * copies_int。copies_int 必须是一个整数,该操
作符返回一个新的包含多份原对象拷贝的对象。
切片操作符 ( []、[:]、[::] )
所谓序列类型就是包含一些顺序排列的对象的一个结构。可以简单的用方括号加一个下标的方式访问它的每一个元素或者通过在方括号中用冒号把开始下标和结束下标分开的方式来访问一组连续的元素。序列类型是其元素被顺序放置的一种数据结构类型,这种方式允许通过指定下标的方式来获得某一个数据元素,或者通过指定下标范围来获得一组序列的元素,这种访问序列的方式叫做切片。
访问某一个数据元素的语法如下:sequence[index],sequence是序列的名字,index是想要访问的元素对应的偏移量。偏移量可以是正值,范围从0到偏移最大值(比序列长度少一),用 len()函数可以得到序列长度,实际的范围是 0 <= inde <=len(sequece)-1 。另外也可以使用负索引,范围是 -1 到序列的负长度,-len(sequence), -len(sequence) <= index <= -1。正负索引的区别在于正索引以序列的开始为起点,负索引以序列的结束为起点,试图访问一个越界的索引会引发异常。
访问多个元素的方法只要简单的给出开始和结束的索引值,并且用冒号分隔就可以了,其语法如下:sequence[starting_index:ending_index],通过这种方式可以得到从起始索引到结束索引(不包括结束索引对应的元素)之间的
一"片"元素,起始索引和结束索引都是可选的,如果没有提供或者用None作为索引值,切片操作会从序列的最开始处开始或者直到序列的最末尾结束。
sequence或sequence[:]。
用步长索引来进行扩展的切片操作,序列的最后一个切片操作是扩展切片操作,它多出来的第三个索引值被用做步长参数。
可以把这个参数看成跟内建函数 range()里面的步长参数,
1. >>> s = 'abcdefgh'
2. >>> s[::-1] #可以视作"翻转"操作
3. 'hgfedcba'
4. >>> s[::2] #隔一个取一个的操作
5. 'aceg'
(3)内建函数(BIFs)
序列本身就内含了迭代的概念,之所以会这样,是因为迭代这个概念就是从序列、迭代器或者其他支持迭代操作的对象中泛化得来的。由于Python的for循环可以遍历所有的可迭代类型,在(非纯序列对象上)执行 for 循环时就像在一个纯序列对象上执行一样。而且 Python 的很多原来只支持序列作为参数的内建函数现在也开始支持迭代器或者或类迭代器了。我们把这些类型统称为"可迭代对象"。
类型转换:内建函数 list()、str()和 tuple()被用做在各种序列类型之间转换。str()函数在需要把一个对象的可打印信息输出时特别有用,不仅仅是对序列类型对其他类型的对象同样如此。Unicode()是str()函数的unicode版本,它跟str()函数基本一样。list()和tuple()函数在列表类型和元组类型的互换时非常有用。
函数 含义
list(iter) 把可迭代对象转换为列表
str(obj) 把 obj 对象转换成字符串(对象的字符串表示法)
unicode(obj) 把对象转换成 Unicode 字符串(使用默认编码)
basestring() 抽象工厂函数,其作用仅仅是为str和unicode函数提供父类,所以不能被实例化,也不能被调用
tuple(iter) 把一个可迭代对象转换成一个元组对象
可操作:Python为序列类型提供可操作BIFs:enumerate(iter)、len(seq)、max(iter,key=None) or max(arg0,arg1...,key=None)、min(iter, key=None) or min(arg0, arg1.... key=None)、reversed(seq)、sorted(iter,func=None,key=None,reverse=False)、sum(seq, init=0)、zip([it0, it1,... itN])。注意len()、reversed()和sum()函数只能接受序列类型对象作为参数,而剩下的则还可以接受可迭代对象做为参数,另外max()和min()函数也可以接受一个参数列表。
2、字符串
字符串类型是Python里最常见的类型。可以简单地通过在引号间包含字符的方式创建它。Python里面单引号和双引号的作用是相同的,这一点Python不同于其他类Shell的脚本语言。几乎所有的 Python 应用程序都会某种方式用到字符串类型。字符串是一种直接量或者说是一种标量,这意味着Python解释器在处理字符串时是把它作为单一值并且不会包含其他Python类型的。字符串是不可变类型,就是说改变一个字符串的元素需要新建一个新的字符串.字符串是由独立的字符组成的,并且这些字符可以通过切片操作顺序地访问。
字符串的创建和赋值
创建一个字符串就像使用一个标量一样简单,当然也可以把str()作为工厂方法来创建一个字符串并把它赋值给一个变量。
如何访问字符串的值(字符和子串)
Python里面没有字符这个类型,而是用长度为1的字符串来表示这个概念,当然这其实也是一个子串。用方括号加一个或者多于一个索引的方式来获得子串。
如何改变字符串
可以通过给一个变量赋值(或者重赋值)的方式“更新”一个已有的字符串。新的值可能与原有值差不多,也可能跟原有串完全不同。
跟数字类型一样,字符串类型也是不可变的,所以你要改变一个字符串就必须通过创建一个新串的方式来实现。也就是说不能只改变一个字符串的一个字符或者一个子串,然而通过拼凑一个旧串的各个部分来得到一个新串是被允许的。
如何删除字符和字符串
字符串是不可变的,所以不能仅仅删除一个字符串里的某个字符,能做的是清空一个空字符串或者是把剔除了不需要的部分后的字符串组合起来形成一个新串。假设想要从"Hello World!"里面删除小写的'l'
>>> aString = 'Hello World!'
>>> aString = aString[:3] + aString[4:]
>>> aString
'Helo World!'
通过赋一个空字符串或者使用 del 语句来清空或者删除一个字符串:
>>> aString = ''
>>> aString
''
>>> del aString
在大部分应用程序里,没有必要显式的删除字符串,定义这个字符串的代码最终会结束,那时Python会自动释放这些字符串。
3、字符串和操作符
(1)标准类型操作符
其中在做比较操作的时候,字符串是按照 ASCII 值的大小来比较的。
(2)序列操作符切片( [ ] 和 [ : ] )
正向索引时:索引值开始于0,结束于总长度减 1(从0开始索引的),最后一个索引是:final_index = len(aString) - 1= 4 - 1= 3。可以访问到包括start在内到end(不包括 end)的所有字符,假设x是[start:end]中的一个索引值,那么start<= x < end。
反向索引操作时: 索引值
是从-1开始,向字符串的开始方向计数,到字符串长度的负数指是索引的结束
索引值
。最末一个索引(也就是第一个字符)是这样定位的:final_index = -len(aString) = -4。
如果开始索引或者结束索引没有被指定,则分别以字符串的第一个和最后一个索引值为默认值。注意:起始/结束索引都没有指定的话会返回整个字符串。
1. >>> aString = 'abcd'
2. >>> len(aString)
3. 4
4. >>> aString[2:4]
5. 'cd'
6. >>> aString[-3:-1]
7. 'bc'
8. >>> aString[:-1]
9. 'abc'
10. >>> aString[:]
11. 'abcd'
成员操作符(in ,not in)
成员操作符用于判断一个字符或者一个子串(中的字符)是否出现在另一个字符串中。出现则返回 True,否则返回 False。注意:成员操作符不是用来判断一个字符串是否包含另一个字符串的,这样的功能由find()或者index()(还有rfind()和 rindex())函数来完成。
string 模块预定义的字符串:
1. >>> import string
2. >>> string.ascii_uppercase
3. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
4. >>> string.ascii_lowercase
5. 'abcdefghijklmnopqrstuvwxyz'
6. >>> string.ascii_letters
7. 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
8. >>> string.digits
9. '0123456789
连接符( + )(运行时刻字符串连接)
通过连接操作符来从原有字符串获得一个新的字符串,
1. >>> s = 'Spanish' + ' ' + 'Inquisition' + ' Made Easy'
2. >>> import string
3. >>> string.upper(s[:3] + s[20])
4. 'SPAM'
其中用一个字符串s的两个切片来构成一个新串的操作,从"Spanish"里面切出"Spa"加上从"Made"里面切出来的"M"。将抽取出来字符串切片连接后作为参数传给了string.upper()方法,该方法负责把字符串的所有字符都变为大写。
string。
1. >>> '%s %s' % ('Spanish', 'Inquisition')
2. 'Spanish Inquisition'
3. >>> s = ' '.join(('Spanish', 'Inquisition', 'Made Easy'))
4. >>> ('%s%s' % (s[:3], s[20])).upper()
5. 'SPAM'
编译时字符串连接
Python 中还有一种并不是经常用到,更像是一种程序员的习惯用法的语法。Python 的语法允许你在源码中把几个字符串连在一起写,以此来构建新字符串:
1. >>> foo = "Hello" "world!"
2. >>> foo
3. 'Helloworld!'
通过这种方法,可以把长的字符串分成几部分来写,而不用加反斜杠。
也可以在一行里面混用两种分号,这种写法的好处是可以把注释也加进来,如
1. >>> f = urllib.urlopen('http://' # protocol
2. ... 'localhost' # hostname
3. ... ':8000' # port
4. ... '/cgi-bin/friends2.py') # file
urlopen()方法所得到的真实输入是:'http://localhost:8000/cgi-bin/friends2.py'。
普通字符串转化为 Unicode 字符串
如果把一个普通字符串和一个Unicode字符串做连接处理,Python会在连接操作前先把普通字符串转化为Unicode字符串。
1. >>> 'Hello' + u' ' + 'World' + u'!'
2. u'Hello World!'
重复操作符( * )
重复操作符创建一个包含了原有字符串的多个拷贝的新串。
1. >>> 'Ni!' * 3
2. 'Ni!Ni!Ni!'
3. >>> '*'*40
4. '****************************************'
5. >>> print '-' * 20, 'Hello World!', '-' * 20
6. -------------------- Hello World! --------------------
4、只适用于字符串的操作符
(1)格式化操作符( % )
Python风格的字符串格式化操作符,只适用于字符串类型,非常类似于C语言里面的printf()函数的字符串格式化,甚至所用的符号都一样,都用百分号(%),并且支持所有printf()式的格式化操作。
字符串格式化符号:
注:%u/%o/%x/%X 在遇到负数的时候会返回一个有符号字符串。Python支持元组和字典形式的两种格式的输入参数。
格式化操作符辅助指令:
1. >>> "%x" % 108 #十六进制输出
2. '6c'
3. >>> "%#X" % 108
4. '0X6C'
5. >>> '%.2f' % 1234.567890 #浮点数和科学记数法形式输出
6. '1234.57'
7. >>> '%e' % 1234.567890
8. '1.234568e+03'
9. >>> "we are at %d%%" % 100 #整数和字符串输出
10. 'we are at 100%'
11. >>> 'Your host is: %s' % 'earth'
12. 'Your host is: earth'
(2)字符串模板:更简单的替代品
字符串格式化操作符是Python里面处理这类问题的主要手段。然而它也不是完美的,其中的一个缺点是它不是那么直观,即使是现在使用字典形式转换的程序员也会偶尔出现遗漏转换类型符号的错误,比如说用了%(lang)而不是正确的%(lang)s。为了保证字符串被正确的转换,程序员必须明确的记住转换类型参数,比如到底是要转成字符串,整数还是其他什么类型。
由于新式的字符串Template对象的引进使得string模块又重新活了过来,Template对象有两个方法:substitute()和 safe_substitute()。前者更为严谨,在 key 缺少的情况下它会报一个KeyError的异常出来,而后者在缺少 key 时,直接原封不动的把字符串显示出来。
1. >>> from string import Template
2. >>> s = Template('There are ${howmany} ${lang} Quotation Symbols')
3. >>> print s.substitute(lang='Python', howmany=3)
4. There are 3 Python Quotation Symbols
(3)原始字符串操作符( r/R )
原始字符串是为了对付那些在字符串中出现的特殊字符,在原始字符串里,所有的字符都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。原始字符串的这个特性让一些工作变得非常的方便,比如正则表达式的创建。除了原始字符串符号(引号前面的字母"r")以外,原始字符串跟普通字符串有着几乎完全相同的语法。这个'r'可以是小写也可以是大写,唯一的要求是必须紧靠在第一个引号前。
1. >>> print '\tmp\read'
2. ead mp
3. >>> print r'\tmp\read'
4. \tmp\read
因为'\t'和'\r'被当成特殊符号,但它们实际上文是4个独立的字符。
1. >>> import re
2. >>> m = re.search(r'\\[rtfvn]', r'Hello World!\n')
3. >>> if m is not None: m.group()
4. ...
5. '\\n'
要找一对原始的\n字符而不是换行。为了找到它,使用了一个简单的正则表达式,它的作用是查找通常被用来表示空白字符的反斜线-字符对(backslash-characterpairs)。
(4)Unicode字符串操作符( u/U )
Unocide字符串操作符:用来把标准字符串或者是包含Unicode字符的字符串转换成完全地Unicode字符串对象,Unicode 操作符也可以接受原始 Unicode 字符串,但Unicode 操作符必须出现在原始字符串操作符前面,如ur'Hello\nWorld!'。
5、内建函数(字符串)
(1)标准类型函数
cmp():同比较操作符一样,内建的cmp()函数也根据字符串的ASCII码值进行比较。
(2)序列类型函数
内建函数len()返回字符串的字符数。 max()和min()函数对其他的序列类型可能更有用,但对于string类型它们能很好地运行,返回最大或者最小的字符(按照 ASCII 码值排列)。
1. >>> s = 'foobar'
2. >>> for i, t in enumerate(s):
3. ... print i, t
4. ...
5. 0 f
6. 1 o
7. 2 o
8. 3 b
9. 4 a
10. 5 r
11. >>> s, t = 'foa', 'obr'
12. >>> zip(s, t)
13. [('f', 'o'), ('o', 'b'), ('a', 'r')]
(3)字符串类型函数
内建的raw_input()函数使用给定字符串提示用户输入并将这个输入返回。
str()和unicode()函数都是工厂函数,就是说产生所对应的类型的对象。它们接受一个任意类型的对象,然后创建该对象的可打印的或者Unicode的字符串表示。它们和basestring都可以作为参数传给isinstance()函数来判断一个对象的类型。
chr()函数用一个范围在range(256)内的(就是0到255)整数做参数,返回一个对应的字符。unichr()跟它一样,只不过返回的是Unicode字符,ord()函数是chr()函数(对于 8 位的 ASCII 字符串)或unichr()函数(对于 Unicode 对象)的配对函数,它以一个字符(长度为 1 的字符串)作为参数,返回对应的ASCII数值或者Unicode数值,如果所给的Unicode字符超出了Python定义范围,则会引发一个TypeError的异常。
(4)字符串内建函数
字符串方法实现了string模块中的大部分方法,所有这些方法都包含了对 Unicode 的支持,有一些甚至是专门用于Unicode的。
6、字符串的独特特性
(1)特殊字符串和控制字符
像其他高级语言和脚本语言一样,一个反斜线加一个单一字符可以表示一个特殊字符,通常是一个不可打印的字符。特殊字符,包括反斜杠转义的那些都可以像普通字符一样存储到 Python 的字符串中。
跟C字符串的另一个不同之处是Python的字符串并不是以NUL(\000)作为结束符的。NUL跟其他的反斜杠转义字符没什么两样。事实上,一个字符串中不仅可以出现NUL字符,而且还可以出现不止一次,在字符串的任意位置都可以。
控制字符的一个作用是用做字符串里面的定界符,在数据库或者web应用中,大多数的可打印字符都是被允许用在数据项里面的,就是说可打印的字符不适合做定界符。使用那些不经常使用的,不可打印的ASCII码值来作为定界符,它们是非常完美的定界符,这样一来诸如冒号这样的可打印字符就可以解脱出来用在数据项中了。
(2)三引号
可以用单引号或者双引号来定义字符串,但是如果你需要包含诸如换行符这样的特殊字符时,单引号或者双引号就不是那么方便了。Python 的三引号就是为了解决这个问题的,它允许一个字符串跨多行,字符串中可以包含换行符、制表符以及其他特殊字符。
(3)字符串不变性
字符串是一种不可变数据类型,就是说它的值是不能被改变或修改的。这就意味着如果你想修改一个字符串或者截取一个子串,或者在字符串的末尾连接另一个字符串等等,必须新建一个字符串。
Python会自动管理内存,不需要知道到底发生了什么,每次你修改一个字符串或者做一些改变字符串内容的操作时,Python 都会自动为你分配一个新串。
1. >>> 'abc' + 'def'
2. 'abcdef'
Python分别为"abc"和"def"分配了空间,当进行连接操作时,Python自动为新的字符串"abcdef"分配了空间。
1. >>> s = 'abc'
2. >>> s = s + 'def'
3. >>> s
4. 'abcdef'
先把"abc"赋给了s,然后在s的末尾添加了"def"。这样看起来字符串似乎是可变的,其实事实是在"s+'def""这个操作进行的时候,新建了一个新字符串,然后这个新的对象被赋给了s,原来的字符串'abc'被析构掉了。
对像字符串这样的不可变对象,我们探究了它在赋值操作中所为左值的限制,左值必须是一个完整的对象,比如说一个字符串对象,不能是字符串的一部分。对赋值操作的右值没有这个限制。
7、Unicode
从Python1.6起引进的Unicode字符串支持,是用来在多种双字节字符的格式、编码进行转换的,其中包括一些对这类字符串的操作管理功能。内建的字符串和正则表达式对Unicode字符串的支持,再加上string模块的辅助,Python已经可以应付大部分应用对Unicode的存储、访问、操作的需要了。
(1)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个字符。
(2)使用Unicode
Python里面处理Unicode字符串跟处理ASCII字符串没什么两样。Python把硬编码的字符串叫做字面上的字符串,默认所有字面上的字符串都用ASCII编码,可以通过在字符串前面加一个'u'前缀的方式声明Unicode字符串,这个'u'前缀告诉Python后面的字符串
要编码成 Unicode字符串。
内建的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字符串。
(3)编码解码
Unicode支持多种编码格式,这为程序员带来了额外的负担,每当你向一个文件写入字符串的时候,必须定义一个编码(encoding 参数)用于把对应的Unicode内容转换成你定义的格式,Python通过Unicode字符串的encode()函数解决了这个问题,该函数接受字符串中的字符为参数,输出你指定的编码格式的内容。
所以,每次写一个Unicode字符串到磁盘上都要用指定的编码器给他"编码"一下,相应地,当从这个文件读取数据时,必须"解码"该文件,使之成为相应的Unicode字符串对象。
(4)把Unicode应用到实际应用中
处理Unicode字符串遵守的规则:一、程序中出现字符串时一定要加个前缀u。二、不要用 str()函数,用 unicode()代替。三、不要用过时的 string 模块 -- 如果传给它的是非 ASCII 字符,它会把一切搞砸。四、不到必须时不要在你的程序里面编解码Unicod字符,只在要写入文件或数据库或者网络时,才调用 encode()函数。相应地只在需要把数据读回来的时候才调用 decode()函数。(5)Python的Unicode支持
内建的unicode()函数
Unicode 的工厂方法,同Unicode字符串操作符(u/U)的工作方式很类似,它接受一个string做参数,返回一个Unicode字符串。
内建的 decode()/encode()方法
decode()和encode()内建函数接受一个字符串做参数返回该字符串对应的解码后/编码后的字符串。decode()和encode()都可以应用于常规字符串和Unicode字符串。
Unicode类型
Unicode字符串对象是basestring的子类、Unicode()工厂方法或直接在字符串前面加用一个u或者U来创建实例。支持Unicode原始字符串,只要在你的字符串前面加一个ur或者UR就可以了。
Unicode 序数
标准内建函数ord()工作方式相同,已经升级到可以支持Unicode 对象了。内建的unichr()函数返回一个对应的Unicode字符(需要一个 32 位的值),否则就产生一个ValueError异常。
强制类型转换
混合类型字符串操作需要把普通字符串转换成 Unicode 对象。
异常
UnicodeError异常是在exceptions模块中定义的,ValueError的子类。所有关于Unicode编解码的异常都要继承自UnicodeError。
RE引擎对Unicode的支持
正则表达式引擎需要Unicode支持,常用 Unicode 编辑码:
字符串格式化操作符
对于Python的格式化字符串的操作符,%s 把Python字符串中的Unicode对象执行了str(u)操作,所以输出的应该是u.encode(默认编码)。如果格式化字符串是Unicode对象,所有的参数都将首先强制转换成Unicode,然后根据对应的格式串一起进行格式转换。数字首先被转换成普通字符串,然后在转换成Unicode.Python字符串通过默认编码格式转化成Unicode.Unicode对象不变,所有其他格式字符串都需要像上面这样转化,如u"%s %s" % (u"abc", "abc") u"abc abc"。
8、字符串关键点总结
(1)一些引号分隔的字符
可以把字符串看成是Python的一种数据类型,在Python单引号或者双引号之间的字符数组或者是连续的字符集合。在Python中最常用两个引号是单引号(')和双引号(")。字符串的实际内容是这些单引号(')或者双引号(")之间的字符,不包括引号本身。
可以用两种引号来创建字符串是很有益处的,因为是当你的字符串中包含单引号时,如果用单引号创建字符串,那么字符串中的双引号就不需要转义,反之亦然。
(2)不可分字符类型
字符串是唯一的字面上的字符序列类型。不过,字符本身并不是一种类型,所以字符串是字符存储操作的最基本单位。字符应该视为长度为 1 的字符串。
(3)字符串格式化操作符 ( % )提供类似于 printf()那样的功能
字符串格式化操作符提供了一种基于多种输入类型的创建自定义字符串的灵活方式。它也提供了类似于C/C++世界里的格式化操作的接口。
(4)三引号
在三引号字符串中可以包含诸如换行回车或者 tab 键这样的特殊字符。三引号字符串是用两边各三个单引号(''')或者两边各三个双引号(""")来定义的。
(5)原始字符串对每个特殊字符串都使用它的原意
原始字符串并不通过反斜线转义特殊字符的特性。这个特性使得原始字符串非常适用于那些需要字符串原意的场合,比如在定义一个正则表达式时。
(6)Python 字符串不是通过 NUL 或者'\0'来结束的
C编程的一个主要问题是你访问了一个字符串后面的本不属于你的空间,这种情况发生在你没有在字符串末尾添加终结符NUL 或者'\0'(ASCII 值为 0)的时候。Python不仅会自动管理内存,而且也把 C 的这个负担或者说是小麻烦去掉了。Python中的字符串不是以NUL结束的,所以不需要为是否已经添加终结符担心。字符串中只包含你所定义的东西,没有别的。
9、列表
像字符串类型一样,列表类型也是序列式的数据类型,可以通过下标或者切片操作来访问某一个或者某一块连续的元素。不同的是:字符串只能由字符组成,且是不可变的(不能单独改变它的某个值),而列表则是能保留任意数目的Python对象的灵活的容器。
创建列表非常简单,向列表中添加元素也是如此。
列表不仅可以包含Python的标准类型,而且可以用用户定义的对象作为自己的元素。列表可以包含不同类型的对象,而且要比 C或者Python自己的数组类型(包含在 array 扩展包中)都要灵活。因为数组类型所有的元素只能是一种类型,列表可以执行pop、empt、sort、reverse等操作。列表也可以添加或者减少元素,还可以跟其他的列表结合或者把一个列表分成几个。可以对单独一个元素或者多个元素执行insert、update或者remove操作。
元组类型在很多操作上都跟列表一样,许多用在列表上的例子在元组上照样能运行。它们的主要不同在于元组是不可变的,或者说是只读的,所以那些用于更新列表的操作,比如用切片操作来更新一部分元素的操作就不能用于元组类型。
(1)如何创建列表类型数据并给它赋值
创建一个列表就像给一个变量赋值一样的简单,写一个列表(空的或者有值的都行),然后赋给一个变量,列表是由方括号([])来定义的,当然也可以用工厂方法来创建它。
1. >>> aList = [123, 'abc', 4.56, ['inner', 'list'], 7-9j]
2. >>> print aList
3. [123, 'abc', 4.5599999999999996, ['inner', 'list'], (7-9j)]
4. >>> aListThatStartedEmpty = []
5. >>> print aListThatStartedEmpty
6. []
7. >>> list('foo')
8. ['f', 'o', 'o']
(2)如何访问列表中的值
列表的切片操作就像字符串中一样,切片操作符([])和索引值或索引值范围一起使用。
(3)如何更新列表
可以通过在等号的左边指定一个索引或者索引范围的方式来更新一个或几个元素,也可以使用 append()方法来追加元素到列表中去。
(4)如何删除列表中的元素或者列表(本身)
要删除列表中的元素,如果你确切的知道要删除元素的素引可以用 del 语句,否则可以用remove()方法。
1. >>> aList = [123, 'abc', 'float replacer', ['inner', 'list'], (7-9j)]
2. >>> del aList[2]
3. >>> aList
4. [123, 'abc', ['inner', 'list'], (7-9j)]
5. >>> aList.remove('abc')
6. >>> aList
7. [123, ['inner', 'list'], (7-9j)]
还可以通过pop()方法来删除并从列表中返回一个特定对象。一般来说,程序员不需要去删除一个列表对象。列表对象出了作用域(比如程序结束,函数调用完成等等)后它会自动被析构,但是如果你想明确的删除一整个列表,可以用del语句:del aList。
10、列表操作符
(1)标准类型操作符
1. >>> list1 = ['abc', 123]
2. >>> list2 = ['xyz', 789]
3. >>> list3 = ['abc', 124]
4. >>> list1 < list2
5. True
6. >>> list1 < list3
7. True
在使用比较操作符时 ,
比较数字和字符串是很明了的
,
但
在列表上时就不是那么简单了
。
列表比较操作有些狡猾
,
但是合乎逻辑
,
比较列表时也是用的内建的 cmp()函数
,
基本的比较逻辑是
:
两个列表的元素分别比较,
直到有一方的元素胜出。
(2)序列类型操作符
切片([] 和[:])
列表的切片操作跟字符串的切片操作很像,不过列表的切片操作返回的是一个对象或者是几个对象的集合,而不是像字符串那样返回一个字符或者一个子串。
列表的切片操作也遵从正负索引规则,也有开始索引值和结束索引值。如果这两个值为空,默认也会分别指到序列的开始和结束位置。
1. >>> mixup_list = [4.0, [1, 'x'], 'beef', -1.9+6j]
2. >>> mixup_list[1]
3. [1, 'x']
4. >>> mixup_list[2:-1]
5. ['beef']
6. >>> mixup_list[2]
7. 'beef'
在列表的元素上也可以使用所有的序列操作符或者在其之上执行序列类型内建的各种操作,不仅可以在一个切片操作的结果之上再进行切片,而且还可以改变这个切片的结果,即使新对象的类型跟原对象不同也可以。这跟多维数组有一些类似。
1. >>> mixup_list[1][1]
2. 'x'
3. >>> mixup_list[1][0]=100
4. >>> mixup_list[1][1]='y'
5. >>> mixup_list
6. [4.0, [100, 'y'], 'beef', (-1.8999999999999999+6j)]
7. >>> mixup_list[0] = [2.0, 'abc']
8. >>> mixup_list
9. [[2.0, 'abc'], [100, 'y'], 'beef', (-1.8999999999999999+6j)]
可以把列表的单一元素替换成一个列表,在列表中进行诸如remove、add和replace的操作都比较自由。还有一点要注意,如果你想以子列表的形式得到一个列表中的一个切片,那需要确保在赋值时等号的左边也是一个列表而不是一个列表的元素。
成员关系操作( in ,not in)
列表中(同样适用于元组),可以检查一个对象是否是一个列表(或者元组)中的成员。
1. >>> mixup_list
2. [[2.0, 'abc'], [100, 'y'], 'beef', (-1.8999999999999999+6j)]
3. >>> 'beef' in mixup_list
4. True
5. >>> 'y' in mixup_list[1]
6. True
7. >>> [100, 'y'] in mixup_list
8. True
注 意 :'x' 并不属于mixup_list,因为'x'本身并不是mixup_list的一个成员,而是mixup_list[1]的成员,mixup_list[1]也是一个列表类型,成员关系操作运算同样适用于元组类型。
连接接操作符( + )
连接操作符允许把多个列表对象合并在一起。注意:列表类型的连接操作也只能在同类型之间进行,不能把两个不同类型的对象连接在一起,即便他们都是序列类型也不行。
可以用extend()方法来代替连接操作符把一个列表的内容添加到另一个中去,使用 extend()方法比连接操作的一个优点是它实际上是把新列表添加到了原有的列表里面,而不是像连接操作那样新建一个列表。list.extend()方法也被用来做复合赋值运算,即是替
换连接操作(+=)。
注意:连接操作符并不能实现向列表中添加新元素的操作,mixup_list + 'new item'是会抛出异常的,之所以是错误的,是因为在连接操作符的左右两边使用了不同类型的值,列表类型+字符串类型,这样的操作是非法的。可以使用内建函数 append()。
重复操作符( * )
重复操作符可能更多的应用在字符串类型中,列表和元组跟字符串同属序列类型,所以需要的时候也可以使用这一操作。
(3)列表类型操作符和列表解析
Python中没有专门用于列表类型的操作符,列表可以使用大部分的对象和序列类型的操作符。此外,列表类型有属于自己的方法,列表才有的构建--列表解析。这种方法是结合了列表的方括弧和for循环,在逻辑上描述要创建的列表的内容。
11、内建函数(列表)
(1)标准类型函数
cmp()
对两个列表的元素进行比较。二、如果比较的元素是同类型的,则比较其值,返回结果。三、如果两个元素不是同一种类型,则检查它们是否是数字(a、若是数字,执行必要的数字强制类型转换,然后比较。b、如果有一方的元素是数字,则另一方的元素"大"(数字是"最小的")。c、否则,通过类型名字的字母顺序进行比较。)。四、如果有一个列表首先到达末尾,则另一个长一点的列表"大"。五、如果用尽了两个列表的元素而且所有元素都是相等的,那么结果就是个平局,就是说返回一个0。
(2)序列类型函数
len()、max()、min()、sorted() 、reversed()、enumerate() 、zip()、sum()、list()、tuple()
list()函数和tuple()函数接受可迭代对象(比如另一个序列)作为参数,并通过浅拷贝数据来创建一个新的列表或者元组。虽然字符串也是序列类型的,但是它们并不是经常用于 list()和tuple()。更多的情况下,它们用于在两种类型之间进行转换,比如你需要把一个已有的元组转成列表类型的(然后你就可以修改它的元素了)或者相反。
(3)列表类型内建函数
不考虑range()函数的话,Python中没有特定用于列表的内建函数,但列表类型对象可以使用大多数的对象和序列的内建函数,并且列表对象有属于它们自己的方法。
在一个列表对象上应用 dir()方法来得到它所有的方法和属性:
1. >>> dir(list) # or dir([])
2. ['__add__', '__class__', '__contains__', '__delattr__',
3. '__delitem__', '__delslice__', '__doc__', '__eq__',
4. '__ge__', '__getattribute__', '__getitem__',
5. '__getslice__', '__gt__', '__hash__', '__iadd__',
6. '__imul__', '__init__', '__iter__', '__le__', '__len__',
7. '__lt__', '__mul__', '__ne__', '__new__', '__reduce__',
8. '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
9. '__setattr__', '__setitem__', '__setslice__', '__str__',
10. 'append', 'count', 'extend', 'index', 'insert', 'pop',
11. 'remove', 'reverse', 'sort']
列表类型内建函数:
Operation
list.append(obj) 向列表中添加一个对象obj
list.count(obj) 返回一个对象 obj 在列表中出现的次数
list.extend(seq) 把序列seq的内容添加到列表中
list.index(obj, i=0,j=len(list)) 返回list[k] == obj的k值,并且k的范围在 i<=k<j;否则引发ValueError异常
list.insert(index, obj) 在索引量为 index 的位置插入对象 obj
list.pop(index=-1) 删除并返回指定位置的对象,默认是最后一个对象
list.remove(obj) 从列表中删除对象obj
list.reverse() 原地翻转列表
list.sort(func=None,key=None,reverse=False) 以指定的方式排序列表中的成员,如果 func 和 key 参数指定,则按照指定的方式比较各个元素,如果 reverse 标志被置为True,则列表以反序排列.
在使用可变对象的方法如sort()、extend()和 reverse()的时候要注意,这些操作会在列表中原地执行操作,也就是说现有的列表内容会被改变,但是没有返回值!
12、列表的特殊特性
用列表构建其他数据结构:列表有容器和可变的特性,这使得它非常灵活,可以用它来构建其他的数据结构: 堆栈和队列
。
(1)堆栈
堆栈是一个后进先出(LIFO)的数据结构,其工作方式就像自助餐厅里面用于放盘子的弹簧支架。把盘子想像成对象,第一个离开堆栈的是最后放上的那个。在栈上"push"元素是个常用术语,意思是把一个对象添加到堆栈中。反之要删除一个元素,可以把它"pop"出堆栈。
1. #!/usr/bin/env python
2. '''
3. $Id$
4. stack.py -- simulate stack data structures using lists
5. '''
6. # create our data structure
7. stack = []
8.
9. #
10. # pushit() -- adds stirng to the stack
11. #
12. def pushit():
13. stack.append(raw_input('Enter new string: '))
14.
15. #
16. # popit() -- removes stirng from the stack
17. #
18. def popit():
19. if len(stack) == 0:
20. print 'Cannot pop from an empty stack!'
21. else:
22. print 'Removed [', stack.pop(), ']'
23.
24. #
25. # viewstack() -- display stack contents
26. #
27. def viewstack():
28. print str(stack)
29.
30. #
31. # showmenu() -- interactive portion of application
32. # displays menu to prompt user and takes
33. # action based on user response
34. #
35. CMDs = {'u' : pushit, 'o' : popit, 'v' : viewstack}
36. def showmenu():
37.
38. # using triple quotes to help us put together
39. # the multi-line string prompt to display
40. prompt = """
41. p(U)sh
42. p(O)p
43. (V)iew
44. (Q)uit
45. Enter choice: """
46.
47. # loop until user quits
48. done = 0
49. while not done:
50.
51. # loop until user choses valid option
52. chosen = 0
53. while not chosen:
54.
55. # if user hits RETURN/Enter, ^C, or ^D (EOF),
56. # pretend they typed 'q' to quit normally
57. try:
58. choice = raw_input(prompt).strip()[0].lower()
59. except (IndexError, EOFError, KeyboardInterrupt):
60. choice = 'q'
61. print '\nYou picked: [%s]' % choice
62.
63. # validate option chosen
64. if choice not in 'uovq':
65. print 'invalid option, try again'
66. else:
67. chosen = 1
68.
69. # take appropriate action
70. choice == 'q':
71. done = 1
72. choice == 'u':
73. # pushit()
74. choice == 'o':
75. # popit()
76. choice == 'v':
77. # viewstack()
78.
79. choice == 'q':
80. done = 1
81. else:
82. CMDs[choice]()
83.
84. # run showmenu() as the application
85. if __name__ == '__main__':
86. showmenu()
1. [root@localhost alt]$ python stack.py
2.
3. p(U)sh
4. p(O)p
5. (V)iew
6. (Q)uit
7. Enter choice: Qt
8.
9. You picked: [q]
10. [aoyang@localhost alt]$ python stack.py
11.
12. p(U)sh
13. p(O)p
14. (V)iew
15. (Q)uit
16. Enter choice: u
17.
18. You picked: [u]
19. Enter new string: 123456
20.
21. p(U)sh
22. p(O)p
23. (V)iew
24. (Q)uit
25. Enter choice: u
26.
27. You picked: [u]
28. Enter new string: 789012
29.
30. p(U)sh
31. p(O)p
32. (V)iew
33. (Q)uit
34. Enter choice: v
35.
36. You picked: [v]
37. ['123456', '789012']
38.
39. p(U)sh
40. p(O)p
41. (V)iew
42. (Q)uit
43. Enter choice: q
44.
45. You picked: [q]
(2) 队列
队列是一种先进先出(FIFO)的数据类型,它的工作原理类似于超市中排队交钱或者银行里面的排队。新的元素通过"入队"的方式
添加进队列的末尾,"出队"就是从队列的头部删除。
用列表实现了一个简单的队列:
1. #!/usr/bin/env python
2. '''
3. $Id$
4.
5. queue.py -- simulate queue data structures using lists
6.
7. NOTE: as of the time of publication, there is a bug in JPython1.1
8. that does not recognize arguments for the list pop() method:
9. TypeError: pop(): expected 0 args; got 1
10. '''
11.
12. # create our data structure
13. queue = []
14.
15. #
16. # enQ() -- add string to end of queue
17. #
18. def enQ():
19. queue.append(raw_input('Enter new queue element: '))
20.
21. #
22. # deQ() -- remove string from front of queue
23. #
24. def deQ():
25. if len(queue) == 0:
26. print 'Cannot dequeue from empty queue!'
27. else:
28. print 'Removed [', queue.pop(0), ']'
29.
30. #
31. # viewQ() -- display queue contents
32. #
33. def viewQ():
34. print str(queue)
35.
36. #
37. # showmenu() -- interactive portion of application
38. # displays menu to prompt user and takes
39. # action based on user response
40. #
41.
42. def showmenu():
43. prompt = """
44. (E)nqueue
45. (D)equeue
46. (V)iew
47. (Q)uit
48. Enter choice: """
49.
50. # loop until user quits
51. done = 0
52. while not done:
53.
54. # loop until user choses valid option
55. chosen = 0
56. while not chosen:
57.
58. # if user hits ^C or ^D (EOF),
59. # pretend they typed 'q' to quit
60. try:
61. choice = raw_input(prompt).strip()[0].lower()
62. except (IndexError, EOFError, KeyboardInterrupt):
63. choice = 'q'
64. print '\nYou picked: [%s]' % choice
65.
66. # validate option chosen
67. if choice not in 'devq':
68. print 'invalid option, try again'
69. else:
70. chosen = 1
71.
72. # take appropriate action
73. choice == 'q': done = 1
74. choice == 'e': enQ()
75. choice == 'd': deQ()
76. choice == 'v': viewQ()
77.
78. # run showmenu() as the application
79. if __name__ == '__main__':
80. showmenu()
1. [root@localhost alt]$ python queue.py
2.
3. (E)nqueue
4. (D)equeue
5. (V)iew
6. (Q)uit
7. Enter choice: E
8.
9. You picked: [e]
10. Enter new queue element: 123
11.
12. (E)nqueue
13. (D)equeue
14. (V)iew
15. (Q)uit
16. Enter choice: E
17.
18. You picked: [e]
19. Enter new queue element: 456
20.
21. (E)nqueue
22. (D)equeue
23. (V)iew
24. (Q)uit
25. Enter choice: E
26.
27. You picked: [e]
28. Enter new queue element: 789
29.
30. (E)nqueue
31. (D)equeue
32. (V)iew
33. (Q)uit
34. Enter choice: D
35.
36. You picked: [d]
37. Removed [ 123 ]
38.
39. (E)nqueue
40. (D)equeue
41. (V)iew
42. (Q)uit
43. Enter choice: v
44.
45. You picked: [v]
46. ['456', '789']
47.
48. (E)nqueue
49. (D)equeue
50. (V)iew
51. (Q)uit
52. Enter choice: q
53.
54. You picked: [q]
13、元组
实际上元组是跟列表非常相近的另一种容器类型,元组和列表看起来不同的一点是元组用的是圆括号而列表用的是方括号。而功能上,元组和列表相比有一个很重要的区别,元组是一种不可变类型。正因为这个原因,元组能做一些列表不能做的事情:用做一个字典的key。另外当处理一组对象时,这个组默认是元组类型。
如何创建一个元组并给它赋值:创建一个元组并给他赋值实际上跟创建一个列表并给它赋值完全一样,除了一点:只有一个元素的元组需要在元组分割符里面加一个逗号(,)用以防止跟普通的分组操作符混淆。不要忘了它是一个工厂方法!
1. >>> emptiestPossibleTuple = ('name')
2. >>> emptiestPossibleTuple
3. 'name'
4. >>> emptiestPossibleTuple = ('name',)
5. >>> emptiestPossibleTuple
6. ('name',)
如何访问元组中的值:元组的切片操作跟列表一样,用方括号作为切片操符([]),里面写上索引值或者索引范围。
如何更新元组:跟数字和字符串一样,元组也是不可变类型,就是说不能更新或者改变元组的元素,只能通过现有 元组 的片段再构造一个新
元组
的方式解决的。
如何移除一个元组的元素以及元组本身:删除一个单独的元组元素是不可能的,当然把不需要的元素丢弃后,重新组成一个元组是没有问题的。要显示地删除一整个元组,只要用del语句减少对象引用计数,当这个引用计数达到0的时候,该对象就会被析构。大多数时候,不需要显式的用del删除一个对象,一出它的作用域它就会被析构,Python编程里面用到显式删除元组的情况非常之少,如:del aTuple。
14、元组操作符和内建函数
(1)标准类型操作符、序列类型操作符和内建函数
元组的对象和序列类型操作符还有内建函数跟列表的完全一样。仍然可以对元组进行切片、合并操作以及多次拷贝一个元组,还可以检查一个对象是否属于一个元组,进行元组之间的比较等。
(2)元组类型操作符和内建函数,内建方法
像列表一样元组也没有它自己专用的运算符和内建函数。
15、元组的特殊特性
(1)不可变性给元组带来了什么影响
一个数据类型是不可变的,简单来讲,就意味着一旦一个对象被定义了,它的值就不能再被更新,除非重新创建一个新的对象。对数字和字符串的影响不是很大,因为它们是标量类型,当它们代表的值改变时,这种结果是有意义的,是按照你所想要的方式进行访问的。而对于元组它是容器对象,很多时候想改变的只是这个容器中的一个或者多个元素,但元组无法实现, 切片操作符不能用作左值进行赋值 ,
切片操作只能用于只读的操作
。
不可变并不是坏事,比如我们把数据传给一个不了解的API时,可以确保我们的数据不会被修改。同样地,如果我们操作从一个函数返回的元组,可以通过内建list()函数把它转换成一个列表。
(2)元组也不是那么“不可变”
虽然元组是被定义成不可变的,但这并不影响它的灵活性。元组并不像我们想的那么不可变,比如说,既然我们可以把字符串组合在一起形成一个大字符串。那么把元组组合在一起形成一个大的元组,所以连接操作可用,这个操作一点都没有改变那些小元组。
元组 也适用于重复操作,重复操作只不过是多次复制同样的元素,再有可以用一个简单的函数调用把一个元组变成一个可变的列表,就可以“修改”特定的元组元素了。虽然元组对象本身是不可变的,但这并不意味着元组包含的可变对象也不可变了。
1. >>> t = ('third', 'fourth')
2. >>> t = t + ('fifth', 'sixth')
3. >>> t
4. ('third', 'fourth', 'fifth', 'sixth')
5. >>> t = (['xyz', 123], 23, -103.4)
6. >>> t[0][1]
7. 123
8. >>> t[0][1] = ['abc', 'def']
9. >>> t
10. (['xyz', ['abc', 'def']], 23, -103.40000000000001)
(3)默认集合类型
所有的多对象的、逗号分隔的、没有明确用符号定义的,比如说像用方括号表示列表和用圆括号表示元组一样等等,这些集合默认的类型都是元组。如:
1. >>> 'abc', -4.24e93, 18+6.6j, 'xyz'
2. ('abc', -4.2400000000000003e+93, (18+6.5999999999999996j), 'xyz')
3. >>> x, y = 1, 2
4. >>> x, y
5. (1, 2)
所有函数返回的多对象(不包括有符号封装的)都是元组类型。注意:有符号封装的多对象集合其实是返回的一个单一的容器对象。如return obj1, obj2, obj3等价于return (obj1, obj2, obj3)。
为了避免副作用,建议总是显式的用圆括号表达式表示元组或者创建一个元组。
1. >>> 4, 2 < 3, 5 # int, comparison, int
2. (4, True, 5)
3. >>> (4, 2) < (3, 5) # tuple comparison
4. False
其中小于号的优先级高于逗号,因此2<3的结果成了元组变量的第二个元素,适当的封装元组就会得到希望得到的结果。
(4)单元素元组
因圆括号被重载了,它也被用作分组操作符。由圆括号包裹的一个单一元素首先被作为分组操作,而不是作为元组的分界符,一个变通的方法是在第一个元素后面添一个逗号(,)来表明这是一个元组而不是在做分组操作。
(5)作为字典的关键字
不可变对象的值是不可改变的。这就意味着它们通过 hash 算法得到的值总是一个值。这是作为字典键值的一个必备条件。因为键值必须是可哈希的对象,元组变量符合这个标准,而列表变量就不行。
16、元组的特殊特性
列出了与序列类型相关的关键模块,其中copy模块负责处理对象的浅拷贝和深拷贝。
operator模块除了提供与数字操作符相同的功能外,还提供了与序列类型操作符相同的功能。types模块是代表python支持的全部类型的type对象的引用。最后,UserList模块包含了list对象的完全的类实现。因为Python类型不能作为子类,所以这个模块允许用户获得类似list的类,也可以派生出新的类或功能。
16、拷贝 Python 对象、浅拷贝和深拷贝
对象赋值实际上是简单的对象引用,也就是说当你创建一个对象,然后把它赋给另一个变量的时候,Python并没有拷贝这个对象而是拷贝了这个对象的引用。
比如,想创建一对小夫妻的通用档案,名为 person,然后分别为它俩拷贝一份。有两种拷贝对象的方式:一种是切片操作,另一种是工厂方法,为了区分出三个不同的对象,使用 id()内建函数来显示每个对象的标识符(也可用is操作来实现)。
1. >>> person = ['name', ['savings', 100.00]]
2. >>> hubby = person[:] #slice copy
3. >>> wifey = list(person) #fac func copy
4. >>> [id(x) for x in person, hubby, wifey]
5. [139947774005832, 139947774003032, 139947877806880]
为它们创建了初始有100元的个人存款帐户。用户名改为定制的名字。但是,当丈夫取走50元后,他的行为影响到了他妻子的账户,虽然我们进行了分开的拷贝,但结果却不是所期望的(希望他们每个人都拥有自己单独的帐号,而不是一个单一的联合帐号)。如下:
1. >>> hubby[0] = 'joe'
2. >>> wifey[0] = 'jane'
3. >>> hubby, wifey
4. (['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
5. >>> hubby[1][1] = 50.00
6. >>> hubby, wifey
7. (['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])
原因是仅仅做了一个浅拷贝。对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原来对象元素的引用。换句话说,这个拷贝的对象本身是新的,但是它的内容却不是。序列类型对象的浅拷贝是默认类型拷贝,在以下几种方式下实施:一、完全切片操作[:];二、利用工厂函数,比如 list(),dict()等;三、使用copy模块的copy函数。
上述 的下一个问题可能是: 当妻子的名字被赋值
,
为什么丈夫的名字没有受到影响
?
难道它们的名字现在不应该都是'jane'了吗
?
为什么名字没有变成一样的呢?怎么会是这样呢?
这是因为
在这两个列表的两个对象中
,
第一个对象是不可变的
(
是个字符串类型
),
而第二个是可变的
(
一个列表
),
正因为如此
,
当进行浅拷贝时,
字符串被显式的拷贝,并新创建了一个字符串对象,而列表元素只是把它的引用复制了一下,并不是它的成员。所以改变名字没有任何问题,但是更改他们银行账号的任何信息都会引发问题。现在,让我们分别看一下每个列表的元素的对象ID值,注意:银行账号对象是同一个对象,这也是为什么对一个对象进行修改会影响到另一个的原因。注意:在我们改变他们的名字后,新的名字字符串是如何替换原有'名字'字符串的。
比较前后对象的不同:
1. >>> person = ['name', ['savings', 100.00]]
2. >>> hubby = person[:] #slice copy
3. >>> wifey = list(person) #fac func copy
4. >>> [id(x) for x in hubby]
5. [139947878070384, 139947774008568]
6. >>> [id(x) for x in wifey]
7. [139947878070384, 139947774008568]
8. >>> hubby[0] = 'joe'
9. >>> wifey[0] = 'jane'
10. >>> hubby[1][1] = 50.00
11. >>> [id(x) for x in hubby]
12. [139947773975264, 139947774008568]
13. >>> [id(x) for x in wifey]
14. [139947773973680, 139947774008568]
假设要给这对夫妻创建一个联合账户,那这是一个非常棒的方案,但是如果需要的是两个分离账户,就需要作些改动了。要得到一个完全拷贝或者说深拷贝--创建一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用--需要 copy.deepcopy()函数。
深拷贝的例子:
1. >>> person = ['name', ['savings', 100.00]]
2. >>> hubby = person
3. >>> import copy
4. >>> wifey = copy.deepcopy(person)
5. >>> [id(x) for x in person, hubby, wifey]
6. [139947774002528, 139947774002528, 139947774008496]
7. >>> [id(x) for x in hubby]
8. [139947878070384, 139947877786544]
9. >>> [id(x) for x in wifey]
10. [139947878070384, 139947774008064]
11. >>> hubby, wifey
12. (['name', ['savings', 100.0]], ['name', ['savings', 100.0]])
13. >>> hubby[1][1] = 50.00
14. >>> hubby, wifey
15. (['name', ['savings', 50.0]], ['name', ['savings', 100.0]])
可以确认一下所有四个对象都是不同的。
以下有几点关于拷贝操作的警告: 第一 ,非容器类型(比如数字、字符串和其他"原子"类型的对象,像代码、类型和xrange对象等)没有被拷贝一说,浅拷贝是用完全切片操作来完成的;第二,如果元组变量只包含原子类型对象,对它的深拷贝将不会进行,如果我们把账户信息改成元组类型,那么即便按我们的要求使用深拷贝操作也只能得到一个浅拷贝。
1. >>> person = ['name', ('savings', 100.00)]
2. >>> newPerson = copy.deepcopy(person)
3. >>> [id(x) for x in person, newPerson]
4. [139947774008424, 139947774008640]
5. >>> [id(x) for x in person]
6. [139947878070384, 139947773984064]
7. >>> [id(x) for x in newPerson]
8. [139947878070384, 139947773984064]
核心模块: copy模块
,其中函数
copy()进行浅拷贝操作,只拷贝父对象不会拷贝对象的内部的子对象;
函数deepcopy()进行深拷贝操作,拷贝对象及其子对象。
1. #!/usr/bin/env python
2. #coding:utf-8
3. # -*- coding: utf-8 -*-
4.
5. '''
6. copy.py -- copy and deepcopy
7. '''
8.
9. import copy
10. a = [1, 2, 3, 4, ['a', 'b']] #原始对象
11.
12. b = a #赋值,传对象的引用
13. c = copy.copy(a) #对象拷贝,浅拷贝
14. d = copy.deepcopy(a) #对象拷贝,深拷贝
15.
16. a.append(5) #修改对象a
17. a[4].append('c') #修改对象a中的['a', 'b']数组对象
18.
19. print 'a = ', a
20. print 'b = ', b
21. print 'c = ', c
22. print 'd = ', d
输出结果:
1. a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
2. b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
3. c = [1, 2, 3, 4, ['a', 'b', 'c']]
4. d = [1, 2, 3, 4, ['a', 'b']]
17、序列类型小结
序列类型为数据的顺序存储提供了几种机制。
其中字符串是最常用的数据载体,无论是用于给用户显示、存贮到硬盘、通过网络传输还是作为一个多源信息的容器。
列表和元组提供了容器存储能力,允许简单的操作和访问多个对象,无论它们是Python的对象还是用户自定义的对象。单一
元素或一组元素可以通过持续有序地索引偏移进行切片操作来访问。
列表和 元组
的区别:
使用不可变类型变量
(元组)的是
如果需要
维护一些敏感的数据
,
并且需要把这些
数据传递给一个并不了解的函数
或API,
要确保传递的数据不会被调用的函数篡改,就使用元组。
需要可变类型
参数(列表)
的是
在管理动态数据集合时
,
需要先把它们创建出
来
,
逐渐地或者不定期的添加它们
或者
有时还要移除一些单个的元素,
这就必须使用可变类型对象
。同时
内建的list()和tuple()转换函数
在两者之间进行转换
,
有一个元组变量但需要一个列表变量,因为你要更新一下它的对象,这时list()函数就是你最好的帮手;
有一个列表变量
,想把它传递给一个函数
或一个API,
又不想让任何人弄乱你的数据,这时tuple()函数就非常有用。
总之,这些数据类型为Python开发环境提供了灵活而易用的存贮工具。
三、总结
(1)字符串、元组、列表再加上字典,构成了Python的主要的存储方式。
(2)Python的浅拷贝和深拷贝在项目开发中非常重要,涉及到数据的同步维护。
####################################