@zhengyuhong
2014-10-17T02:39:24.000000Z
字数 21310
阅读 1174
Python 读书笔记
在交互式解释器中显示变量的值
>>> myString ='hello world'>>> print myStringhello world>>> myString'hello world'
注意,在仅用变量名显示内容时,输出的字符串时用单引号括起来的,当字符串中有单引号时用双引号括起来。这也是为了让非字符串对象也能以字符串的方式显示在屏幕,显示的是该对象的字符串表示,而不是字符串本身。print语句调用str()显示对象,而交互式解释器则调用repr()函数来显示对象。
下划线(_)在解释器中有特别的含义,表示最后一个表达式的值。所以上面的代码执行之后,
下划线变量会包含字符串:
>>> _Hello World!
Python的print语句,与字符串格式运算符( %)结合使用,可实现字符串替换功能,这一点和 C 语言中的printf()函数非常相似:
>>> print "%s is number %d!" % ("Python", 1)Python is number 1!
Print 语句也支持将输出重定向到文件。这个特性是从Python2.0开始新增的。符号 >> 用来重定向输出,下面这个例子将输出重定向到标准错误输出:
import sysprint >> sys.stderr, 'Fatal error: invalid input!'
下面是一个将输出重定向到日志文件的例子
logfile = open('/tmp/mylog.txt', 'a')print >> logfile, 'Fatal error: invalid input!'logfile.close()
从用户那里得到数据输入的最容易的方法是使用raw_input()内建函数。它读取标准输入,并将读取到的数据赋值给指定的变量。 你可以使用 int()内建函数将用户输入的字符串转换为整数。
Python中字符串被定义为引号之间的字符集合。Python支持使用成对的单引号或双引号,三引号(三个连续的单引号或者双引号)可以用来包含特殊字符。使用索引运算符( [ ] )和切片运算符([ : ])可以得到子字符串。字符串有其特有的索引规则:第一个字符的索引是 0,最后一个字符的索引是 -1
加号( + )用于字符串连接运算,星号( * )则用于字符串重复。
可以将列表和元组当成普通的“数组”,它能保存任意数量任意类型的 Python 对象。和数组一样,通过从0开始的数字索引访问元素,但是列表和元组可以存储不同类型的对象。
列表和元组有几处重要的区别。
列表元素用中括号[]包裹,元素的个数及元素的值可以改变。元组元素用小括号()包裹,不可以更改(尽管他们的内容可以,用指针的思想理解)。
元组可以看成是只读的列表。通过切片运算[]和 [:]可以得到子集,这一点与字符串的使用方法一样。
字典是 Python 中的映射数据类型,工作原理类似 Perl 中的关联数组或者哈希表,C++中的map,由键-值(key-value)对构成。几乎所有类型的 Python 对象都可以用作键,不过一般还是以数字或者字符串最为常用。值可以是任意类型的 Python 对象,字典元素用大括号{ }包裹。
Python 提供了一个range()内建函数来生成这种列表。它正好能满足我们的需要, 接受一个数值范围, 生成一个列表。
for i in range(0.50):#第三个位置参数是步长,默认为1,print i
handle = open(file_name, mode = 'r') #'a','w','r',默认值为 'r'
dir([obj]),显示对象的属性,如果没有提供参数, 则显示全局变量的名字help([obj]),以一种整齐美观的形式显示对象的文档字符串,如果没有提供任何参数, 则会进入交互式帮助。int(obj),将一个对象转换为整数len(obj),返回对象的长度open(fn, mode),以mode('r'=读,'w'=写)方式打开一个文件名为fn的文件range([[start,]stop[,step]) 返回一个整数列表。起始值为 start, 结束值为 stop - 1; start 默认值为 0, step默认值为1。raw_input(str),等待用户输入一个字符串,可以提供一个可选的参数str用作提示信息。str(obj),将一个对象转换为字符串type(obj),返回对象的类型(返回值本身是一个 type 对象!)
Python 语句中有一些基本规则和特殊字符:
Python语句,一般使用换行分隔,也就是说一行一个语句。一行过长的语句可以使用反斜杠(\) 分解成几行,如下例:
# check conditionsif (weather_is_hot == 1) and \(shark_warnings == 0):send_goto_beach_mesg_to_pager()
有两种例外情况一个语句不使用反斜线也可以跨行。在使用闭合操作符时,单一语句可以跨多行,例如:在含有小括号、中括号、花括号时可以多行书写。另外就是三引号包括下的字符串也可以跨行书写
缩进相同的一组语句构成一个代码块,我们称之代码组。像 if、while、def 和 class 这样的复合语句,首行以关键字开始,以冒号:结束,该行之后的一行或多行代码构成代码组。我们将首行及后面的代码组称为一个子句(clause)
__xxx 类中的私有变量名
核心风格:避免用下划线作为变量名的开始因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,我们建议程序员避免用下划线作为变量名的开始。一般来讲,变量名_xxx被看作是“私有的”,在模块或类外不可以使用。当变量是私有的时候,用_xxx 来表示变量是很好的习惯。因为变量名__xxx__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
核心笔记:__name__ 指示模块应如何被加载
由于主程序代码无论模块是被导入还是被直接执行都会运行,我们必须知道模块如何决定
运行方向。一个应用程序可能需要导入另一个应用程序的一个模块,以便重用一些有用的代码(否则就只能用拷贝粘贴那种非面向对象的愚蠢手段)。这种情况下,你只想访问那些位于其它应用程序中的代码,而不是想运行那个应用程序。因此一个问题出现了,“Python是否有一种方法能在运行时检测该模块是被导入还是被直接执行呢?” 答案就是......(鼓声雷动).....没错! __name__ 系统变量就是正确答案。
如果模块是被导入, __name__ 的值为模块名字
如果模块是被直接执行, __name__ 的值为 '__main__'
内存管理细节:
动态类型:
还要注意一点,Python 中不但变量名无需事先声明,而且也无需类型声明。Python 语言中, 对象的类型和内存占用都是运行时确定的。尽管代码被编译成字节码,Python 仍然是一种解释型语言。在创建--也就是赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。在对象创建后,一个该对象的应用会被赋值给左侧的变量
引用计数:
要保持追踪内存中的对象, Python 使用了引用计数这一简单技术。也就是说 Python 内部记录着所有使用中的对象各有多少引用。你可以将它想像成扑克牌游戏“黑杰克”或“21 点”。一个内部跟踪变量,称为一个引用计数器。至于每个对象各有多少个引用, 简称引用计数。当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为 0 时,它被垃圾回收。(严格来说这不是 100%正确,不过现阶段你可以就这么认为)
垃圾收集:
不再被使用的内存会被一种称为垃圾收集的机制释放。象上面说的, 虽然解释器跟踪对象的引用计数,但垃圾收集器负责释放内存。垃圾收集器是一块独立代码, 它用来寻找引用计数为 0 的对象。它也负责检查那些虽然引用计数大于 0 但也应该被销毁的对象。 特定情形会导致循环引用。
一个循环引用发生在当你有至少两个对象互相引用时,也就是说所有的引用都消失时,这些引用仍然存在, 这说明只靠引用计数是不够的。Python的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。 当一个对象的引用计数变为0,解释器会暂停,释放掉这个对象
和仅有这个对象可访问(可到达)的其它对象。作为引用计数的补充,垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环
所有的 Python 对像都拥有三个特性:身份,类型和值。
身份:每一个对象都有一个唯一的身份标识自己,任何对象的身份可以使用内建函数 id()来得到。这个值可以被认为是该对象的内存地址。您极少会用到这个值,也不用太关心它究竟是什么。
类型:对象的类型决定了该对象可以保存什么类型的值,可以进行什么样的操作,以及遵循什么样的规则。您可以用内建函数 type()查看 Python对象的类型。因为在 Python 中类型也是对象(还记得我们提到 Python 是面向对象的这句话吗?),所以 type()返回的是对象而不是简单的字符串。
值:对象表示的数据项
核心笔记:布尔值
所有标准对象均可用于布尔测试,同类型的对象之间可以比较大小。每个对象天生具有布尔 True 或 False 值。空对象、 值为零的任何数字或者 Null 对象 None 的布尔值都是 False。
下列对象的布尔值是 False。
a = 4.3b = 4.3a is bTruec = 4.3d = 1.3 + 3.0c is dFalse
>>> a = 1>>> b = 1>>> id(a)34568440L>>> id(b)34568440L>>> a = 1.0>>> id(a)39257928L>>> b = 1.0>>> id(b)39258024L
Python仅仅缓存简单整型,因为Python认为在Python应用程序中这些小整型会被经常用到。
内建函数 cmp()用于比较两个对象 obj1 和 obj2, 如果 obj1 小于 obj2, 则返回一个负整数,如果 obj1 大于 obj2 则返回一个正整数, 如果 obj1 等于 obj2, 则返回 0。它的行为非常类似于 C 语言的strcmp()函数。比较是在对象之间进行的,不管是标准类型对象还是用户自定义对象。如果是用户自定义对象, cmp()会调用该类的特殊方法__cmp__()。
内建函数 str() 和 repr() 或反引号运算符(``)可以方便的以字符串的方式获取对象的内容、类型、数值属性等信息。str()函数得到的字符串可读性好, 而repr()函数得到的字符串通常可以用来重新获得该对象, 通常情况下 obj == eval(repr(obj)) 这个等式是成立的。这两个函数接受一个对象做为其参数, 返回适当的字符串。
>>> class Foo: pass # new-style class...>>> foo = Foo()>>> class Bar(object): pass # new-style class...>>> bar = Bar()>>>>>> type(Foo)<type 'classobj'>>>> type(foo)<type 'instance'>>>> type(Bar)<type 'type'>>>> type(bar)<class '__main__.Bar'>
Python 2.2 统一了类型和类, 所有的内建类型现在也都是类, 在这基础之上, 原来的所谓内建转换函数象 int(), type(), list() 等等,现在都成了工厂函数。 也就是说虽然他们看上去有点象函数, 实质上他们是类。当你调用它们时,实际上是生成了该类型的一个实
例, 就象工厂生产货物一样。下面这些大家熟悉的工厂函数在老的 Python 版里被称为内建函数:
Python 有五个运算内建函数用于数值运算: abs(), coerce(), divmod(), pow(), pow()和 round()。我们将对这些函数逐一浏览,并给出一些有用的例子:
divmod()内建函数把除法和取余运算结合起来,返回一个包含商和余数的元组。对整数来说,它的返回值就是地板除和取余操作的结果。对浮点数来说,返回的商部分是math.floor(num1/num2),对复数来说,商部分是ath.floor((num1/num2).real)。
有两个永不改变的值 True 或 False。
#无 __nonzero__()>>> class C: pass>>> c = C()>>>>>> bool(c) True>>> bool(C) True#重载 __nonzero__() 使它返回 False>>> class C:... def __nonzero__(self):... return False...>>> c = C()>>> bool(c) False>>> bool(C) True
sequence[index]sequence[begin:end]sequence[begin:end:step]#begin,end,step 同号,当负号时表示从后往前索引
序列类型转换工厂函数
#浅层拷贝例子>>> a = [1]>>> b = [a]+[2]>>> c = [b] + [3]>>> d = [c] + [4]>>> e = [d] + [5]>>> e[[[[[1], 2], 3], 4], 5]>>> a[0] = 100>>> e[[[[[100], 2], 3], 4], 5]
序列类型可用的内建函数
序列操作符切片([]和[:] [::])与6.1的一样
成员操作符(in not in)
功能函数find(),index(),rfind(),rindex()
普通字符串转化为Unicode字符串:
如果把一个普通字符串和一个Unicode字符串做连接处理,Python会在连接操作前把普通字符串转化为Unicode字符串,可以使用空Unicode串转化
s = 'hello world'us = s + u''
格式化操作符(%)
格式化字符 转换方式
%c 转换成字符(ASCII 码值,或者长度为一的字符串)
%r 优先用 repr()函数进行字符串转换
%s 优先用 str()函数进行字符串转换
%d / %i 转成有符号十进制数
%u 转成无符号十进制数
%o 转成无符号八进制数
%x/%X (Unsigned)转成无符号十六进制数(x/X 代表转换后的十六进制字符的大
小写)
%e/%E 转成科学计数法(e/E 控制输出 e/E)
%f/%F 转成浮点数(小数部分自然截断)
%g/%G %e和%f/%E 和%F 的简写
%% 输出%
格式化操作符支持两种输入参数,第一种是元组,第二种是字典。
第一种按照位置进行替换,第二种按照键进行替换
接下来的例子里,我们打不开我们的 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()
内建的 str()函数和 chr()函数并没有升级成可以处理Unicode.它们只能处理常规的ASCII 编码字符串,如果一个 Unicode 字符串被作作为参数传给了str()函数,它会首先被转换成 ASCII字符串然后在交给 str()函数.如果该 Unicode 字符串中包含任何不被 ASCII 字符串支持的字符,会导致 str()函数报异常.同样地,chr()函数只能以 0 到 255 作为参数工作.如果你传给它一个超出此范围的值(比如说一个 Unicode 字符),它会报异常
>>> str(u'w是')Traceback (most recent call last):File "<pyshell#5>", line 1, in <module>str(u'w是')UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-2: ordinal not in range(128)
新的内建函数 unicode()和 unichar()可以看成 Unicode 版本的 str()和 chr().Unicode()函数可以把任何 Python 的数据类型转换成一个 Unicode 字符串,如果是对象,并且该对象定义了__unicode__()方法,它还可以把该对象转换成相应的 Unicode 字符串
这些处理 Unicode 字符串的例子简单到让人感到有点假, 事实上,只要你遵守以下的规则,处理 Unicode 就是这么简单:
核心笔记:那些可以改变对象值的可变对象的方法是没有返回值的!
Python 初学者经常会陷入一个误区:调用一个方法就返回一个值.最明显的例子就是
sort():>>> music_media.sort()# 没有输出?>>>
在使用可变对象的方法如 sort(),extend() 和reverse()的时候要注意,这些操作会在列表
中++原地执行++操作,也就是说现有的列表内容会被改变,但是没有返回值!是的,与之相反,字符串方法确实有返回值:
>>> 'leanna, silly girl!'.upper()'LEANNA, SILLY GIRL!'
温习一下,字符串是不可变的 -- 不可变对象的方法是不能改变它们的值的,所以它们必须
返回一个新的对象.如果你确实需要返回一个对象,那么我们建议你看一下 Python2.4 以后加入
的 reversed()和 sorted()内建函数.
它们像列表的方法一样工作,不同的是它们可以用做表达式,因为它们返回一个对象.同时原来的那个列表还是那个列表,没有改变,而你得到的是一个新的对象
序列类型只用数字类型的键(从序列的开始起按数值顺序索引)。映射类型可以用其他对象类型做键;一般最常见的是用字符串做键(keys)。和序列类型的键不同,映射类型的键(keys)直接,或间接地和存储的数据值相关联。但因为在映射类型中,我们不再用"序列化排序"的键(keys),所以映射类型中的数据是无序排列的。
你所能获得的有序集合只能是字典中的键的集合或者值的集合。 方法 Keys() 或 values() 返回一个列表,该列表是可排序的。 你还可以用 items()方法得到包含键、值对的元组的列表来排序。由于字典本身是哈希的,所以是无序的。
从 Python 2.2 版本起, 可以用工厂方法 dict() 来创建字典。 当我们详细讨论 dict()的时候会看到更多的例子,现在来看一个小例子:
>>> fdict = dict((['x', 1], ['y', 2]))>>> fdict{'y': 2, 'x': 1}
从 Python 2.3 版本起, 可以用一个很方便的内建方法 fromkeys() 来创建一个"默认"字典, 字典中元素具有相同的值 (如果没有给出, 默认为 None):
>>> ddict = {}.fromkeys(('x', 'y'), -1)>>> ddict{'y': -1, 'x': -1}>>> edict = {}.fromkeys(('foo', 'bar'))>>> edict{'foo': None, 'bar': None}
要想遍历一个字典(一般用键), 你只需要循环查看它的键, 像这样:
>>> dict2 = {'name': 'earth', 'port': 80}>>>>>>> for key in dict2.keys():... print 'key=%s, value=%s' % (key, dict2[key])...key=name, value=earthkey=port, value=80
从 Python 2.2 开始, 你可以不必再用keys()方法获取供循环使用的键值列表了。 可以用迭代器来轻松地访问类序列对象(sequence-likeobjects),比如字典和文件。只需要用字典的名字就可以在 for 循环里遍历字典。
>>> dict2 = {'name': 'earth', 'port': 80}>>>>>>> for key in dict2:... print 'key=%s, value=%s' % (key, dict2[key])...key=name, value=earthkey=port, value=80
如果我们想访问该字典中的一个数据元素,而它在这个字典中没有对应的键,将会产生一个错误:
>>> dict2['server'] Traceback (innermost last):File "<stdin>", line 1, in ?KeyError: server
在这个例子中,我们试图获得字典中'server'键所对应的值。你从上面的代码知道, 'server'这个键并不存在。检查一个字典中是否有某个键的最好方法是用字典的 has_key()方法, 或者另一种比较好的方法就是从 2.2 版本起用的,in 或 not in 操作符。 has_key() 方法将会在未来的Python 版本中弃用,所以用 in 或 not in 是最好的方法。(读者认为这是为了统一接口,使得使用更加方便)
如果你真想"删除"一个字典,用 del 语句 (介绍见小节 3.5.5)。以下是删除字典和字典元素的例子
del dict2['name'] # 删除键为“name”的条目dict2.clear() # 删除 dict2 中所有的条目del dict2 # 删除整个 dict2 字典dict2.pop('name') # 删除并返回键为“name”的条目
字典可以和所有的标准类型操作符一起工作,但却不支持像拼接(concatenation)和重复(repetition)这样的操作。这些操作对序列有意义,可对映射类型行不通。在接下来的两小节里,我们将向你讲述字典中的操作符。
那么字典又是如何比较的呢? 字典是通过这样的算法来比较的: 首先是字典的大小,然后是键,最后是值。可是,用 cmp() 做字典的比较一般不是很有用。
dict()工厂函数被用来创建字典。如果不提供参数,会生成空字典。当容器类型对象做为一个参数传递给方法 dict()时很有意思。如果参数是可以迭代的,即,一个序列,或是一个迭代器,或是一个支持迭代的对象,那每个可迭代的元素必须成对出现。在每个值对中,第一个元素是字典的键、第二个元素是字典中的值。见 Python 文档里关于 dict()的例子:
我们提醒读者 dict9 的例子只作为了解dict()方法的用途,它不是现实中的例子。使用下面这些行的方法更聪明(效率更好):
>>> dict9 = dict8.copy()
表 7.2 字典类型方法
方法名字 操作
dict.cleara() 删除字典中所有元素
dict.copy() 返回字典(浅复制)的一个副本
dict.fromkeys c(seqval=None) 创建并返回一个新字典,以 seq 中的元素做该字典的键,val做该字典中所有键对应的初始值(如果不提供此值,则默认为 None)
dict.get(key,default=None) 对字典 dict 中的键 key,返回它对应的值value,如果字典中不存在此键,则返回 default 的值(注意,参数 default 的默认值为 None)
dict.has_key(key) 如果键(key)在字典中存在, 返回 True, 否则返回 False. 在 Python2.2版本引入 in 和 not in后,此方法几乎已废弃不用了,但仍提供一个可工作的接口。
dict.items() 返回一个包含字典中(键, 值)对元组的列表
dict.keys() 返回一个包含字典中键的列表
dict.iter() 方法 iteritems(), iterkeys(),itervalues()与它们对应的非迭代方法
一样,不同的是它们返回一个迭代子,而不是一个列表。
dict.pop(key[, default])和方法 get()相似,如果字典中 key 键存在,删除并返回 dict[key],
如果 key 键不存在,且没有给出 default 的值,引发 KeyError 异常dict.setdefault(key,
default=None)和方法 set()相似,如果字典中不存在 key 键,由 dict[key]=default 为它赋值。
dict.update(dict2) 将字典 dict2 的键-值对添加到字典 dictdict.values() 返回一个包含字典中所有值的列表
update()方法可以用来将一个字典的内容添加到另外一个字典中。字典中原有的键如果与新添加的键重复,那么重复键所对应的原有条目的值将被新键所对应的值所覆盖。原来不存在的条目则被添加到字典中。clear()方法可以用来删除字典中的所有的条目。
setdefault()是自 2.0 才有的内建方法,使得代码更加简洁,它实现了常用的语法: 检查字典
中是否含有某键。 如果字典中这个键存在,你可以取到它的值。如果所找的键在字典中不存在,你可以给这个键赋默认值并返回此值。这正是执行 setdefault()方法的目的:
>>> myDict = {'host': 'earth', 'port': 80}>>> myDict.keys()['host', 'port']>>> myDict.items()[('host', 'earth'), ('port', 80)]>>> myDict.setdefault('port', 8080)80>>> myDict.setdefault('prot', 'tcp')'tcp'>>> myDict.items()[('prot', 'tcp'), ('host', 'earth'), ('port', 80)]
目前,keys(), items(), 和values()方法的返回值都是列表。数据集如果很大会导致很难处理,这也正是 iteritems(), iterkeys(), 和 itervalues() 方法被添加到 Python2.2 的主要原因。这些函数与返回列表的对应方法相似,只是它们返回惰性赋值的迭代器,所以节省内存。
大多数 Python对象可以作为键;但它们必须是可哈希的对象。像列表和
字典这样的可变类型,由于它们不是可哈希的,所以不能作为键。所有不可变的类型都是可哈希的, 因此它们都可以做为字典的键。
一个要说明的是问题是数字:值相等的数字表示相同的键。换句话来说,整型数字 1 和 浮点数 1.0 的哈希值是相同的,即它们是相同的键。
同时,也有一些可变对象(很少)是可哈希的,它们可以做字典的键,但很少见。举一个例子,一个实现了 __hash__() 特殊方法的类。因为__hash__()方法返回一个整数,所以仍然是用不可变的值(做字典的键)。
为什么键必须是可哈希的?解释器调用哈希函数,根据字典中键的值来计算存储你的数据的位置。如果键是可变对象,它的值可改变。如果键发生变化,哈希函数会映射到不同的地址来存储数据。如果这样的情况发生,哈希函数就不可能可靠地存储或获取相关的数据。选择可哈希的键的原因就是因为它们的值不能改变。(此问题在 Python FAQ 中也能找到答案)
我们知道数字和字符串可以被用做字典的键,但元组又怎么样呢?我们知道元组是不可变的,但在小节 6.17.2,我们提示过它们也可能不是一成不变的。用元组做有效的键,必须要加限制:元组中只包括像数字和字符串这样的不可变参数,才可以作为字典中有效的键。
集合(sets)有两种不同的类型,可变集合(set) 和不可变集合(frozenset)。如你所想,对可变集合(set),你可以添加和删除元素,对 不可变集合(frozenset)则不允许这样做。请注意,可变集合(set)不是可哈希的,因此既不能用做字典的键也不能做其他集合中的元素。不可变集合(frozenset)则正好相反,即,他们有哈希值,能被用做字典的键或是作为集合中的一个成员。
集合与列表( [ ] )和字典( { } )不同,没有特别的语法格式。列表和字典可以分别用他们自己的工厂方法 list() 和 dict() 创建,这也是集合被创建的唯一方法 -用集合的工厂方法 set()和 frozenset():
>>> s = set('cheeseshop')>>> sset(['c', 'e', 'h', 'o', 'p', 's'])>>> t = frozenset('bookshop')>>> tfrozenset(['b', 'h', 'k', 'o', 'p', 's'])>>> type(s)<type 'set'>>>> type(t)<type 'frozenset'>>>> len(s)6>>> len(s) == len(t)True>>> s == tFalse
我们之前提到过,只有可变集合能被修改。试图修改不可变集合会引发异常。
>>> t.add('z')Traceback (most recent call last):File "<stdin>", line 1, in ?AttributeError: 'frozenset' object has no attribute 'add'
(Union) Update ( |= ),这个更新方法从已存在的集合中添加(可能多个)成员,此方法和 update()等价
保留/交集更新( &= ),保留(或交集更新)操作保留与其他集合的共有成员。此方法和 intersection_update()等价.
差更新 ( –= ),对集合 s 和 t 进行差更新操作 s-=t,差更新操作会返回一个集合,该集合中的成员是集合 s 去
除掉集合 t 中元素后剩余的元素。此方法和 difference_update()等价.
对称差分更新( ^= ),对集合 s 和 t进行对称差分更新操作(s^=t),对称差分更新操作会返回一个集合,该集合中的成员仅是原集合 s 或仅是另一集合 t 中的成员。此方法和symmetric_difference_update()等价.
set() and frozenset()
方法名称 操作
s.issubset(t) 如果 s 是 t 的子集,则返回 True,否则返回 False
s.issuperset(t) 如果 t 是 s 的超集,则返回 True,否则返回 False
s.union(t) 返回一个新集合,该集合是 s 和 t 的并集
s.intersection(t) 返回一个新集合,该集合是 s 和 t 的交集
s.difference(t) 返回一个新集合,该集合是 s 的成员,但不是 t 的成员
s.symmetric_difference(t) 返回一个新集合,该集合是 s 或 t 的成员,但不是 s 和 t 共有的成员
s.copy() 返回一个新集合,它是集合 s 的浅复制
方法名 操作
s.update(t) 用 t 中的元素修改 s, 即,s 现在包含 s 或 t 的成员
s.intersection_update(t) s 中的成员是共同属于 s 和 t 的元素。
s.difference_update(t) s 中的成员是属于 s 但不包含在 t 中的元素
s.symmetric_difference_update(t) s 中的成员更新为那些包含在 s 或 t 中,但不是 s 和 t 共有的元素
s.add(obj) 在集合 s 中添加对象 obj
s.remove(obj) 从集合 s 中删除对象 obj; 如果 obj 不是集合 s 中的元素(obj notin s),将引发 KeyError 错误
s.discard(obj) 如果 obj 是集合 s 中的元素,从集合 s 中删除对象 obj;
s.pop() 删除集合 s 中的任意一个对象,并返回它
s.clear() 删除集合 s 中的所有元素
Python 还提供了 pass 语句( C 中没有提供对应的语句). Python没有使用传统的大括号来标记代码块, 有时,有些地方在语法上要求要有代码, 而 Python 中没有对应的空大括号或是分号( ; )来表示 C 语言中的 "不做任何事" ,如果你在需要子语句块的地方不写任何语句, 解释器会提示你语法错误. 因此, Python 提供了 pass 语句, 它不做任何事情 - 即 NOP , ( No OPeration , 无操作) 我们从汇编语言中借用这个概念. pass 同样也可作为开发中的小技巧, 标记你后来要完成的代码, 例如这样:
def foo_func():passif user_choice == 'do_calc':pass
你可以在 while 和 for 循环中使用 else 语句. 它们是怎么工作的呢? 在循环中使用时, else子句只在循环完成后执行, 也就是说 break 语句也会跳过 else 块.
同样地, for 循环也可以有 else 用于循环后处理(post-processing). 它和 while 循环中的else 处理方式相同. 只要 for 循环是正常结束的(不是通过 break ), else 子句就会执行.
首先让我们看看列表解析的语法:
[expr for iter_var in iterable]>>> map(lambda x: x ** 2, range(6))[0, 1, 4, 9, 16, 25]>>> [x ** 2 for x in range(6)][0, 1, 4, 9, 16, 25]
回想下 odd() 函数, 它用于判断一个数值对象是奇数还是偶数(奇数返回 1 , 偶数返回 0 ):
def odd(n):return n % 2
我们可以借用这个函数的核心操作, 使用 filter() 和 lambda 挑选出序列中的奇数:
>>> seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12]>>> filter(lambda x: x % 2, seq)[11, 9, 9, 9, 23, 9, 7, 11]
和先前的例子一样, 即使不用 filter() 和 lambda,我们同样可以使用列表解析来完成操作,获得想要的数字:
>>> [x for x in seq if x % 2][11, 9, 9, 9, 23, 9, 7, 11]
生成器表达式在 Python 2.4 被引入,它与列表解析非常相似,而且它们的基本语法基本相同;不过它并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生” (yield)出来. 生成器表达式使用了"延迟计算"(lazy evaluation), 所以它在使用内存上更有效. 我们来看看它和列表解析到底有多相似:
列表解析:
[expr for iter_var in iterable if cond_expr]
生成器表达式:
(expr for iter_var in iterable if cond_expr)
经典例子就是:
range(n)xrange(n)
生成器并不会让列表解析废弃, 它只是一个内存使用更友好的结构, 基于此, 有很多使用生成器地方. 下面我们提供了一些使用生成器表达式的例子, 最后例举一个冗长的样例, 从它你可以感觉到 Python 代码在这些年来的变化.
交叉配对样例
生成器表达式就好像是懒惰的列表解析(这反而成了它主要的优势).它还可以用来处理其他列表或生成器, 例如这里的 rows 和 cols :
rows = [1, 2, 3, 17]def cols(): # example of simple generatoryield 56yield 2yield 1
不需要创建新的列表, 直接就可以创建配对. 我们可以使用下面的生成器表达式:
x_product_pairs = ((i, j) for i in rows for j in cols())
现在我们可以循环 x_product_pairs , 它会懒惰地循环 rows 和 cols :
>>> for pair in x_product_pairs:... print pair
重构样例
我们通过一个寻找文件最长的行的例子来看看如何改进代码. 在以前,我们这样读取文件:
f = open('/etc/motd', 'r')longest = 0while True:linelen = len(f.readline().strip())if not linelen:breakif linelen > longest:longest = linelenf.close()return longest
从那时起, 我们认识到如果读取了 所有的行, 那么应该尽早释放文件资源. 如果这是一个很多进程都要用到的日志文件,那么理所当然我们不能一直拿着它的句柄不释放. 是的, 我们的例子是用来展示的, 但是你应该得到这个理念. 所以读取文件的行的首选方法应该是这样:
f = open('/etc/motd', 'r')longest = 0allLines = f.readlines()f.close()for line in allLines:linelen = len(line.strip())if linelen > longest:longest = linelenreturn longest
列表解析允许我们稍微简化我们代码,而且我们可以在得到行的集合前做一定的处理. 在下段代码中, 除了读取文件中的行之外,我们还调用了字符串的 strip() 方法处理行内容.
f = open('/etc/motd', 'r')longest = 0allLines = [x.strip() for x in f.readlines()]f.close()for line in allLines:linelen = len(line)if linelen > longest:longest = linelenreturn longest
然而, 两个例子在处理大文件时候都有问题, 因为 readlines() 会读取文件的所有行. 后来我们有了迭代器, 文件本身就成为了 它自己的迭代器, 不需要调用 readlines() 函数. 我们已经做到了这一步,为什么不去直接获得行长度的集合呢(之前我们得到的是行的集合)? 这样, 我们就可以使用 max() 内建函数得到最长的字符串长度:
f = open('/etc/motd', 'r')allLineLens = [len(x.strip()) for x in f]f.close()return max(allLineLens)
最后, 我们可以去掉文件打开模式(默认为读取), 然后让 Python 去处理打开的文件. 当然,文件用于写入的时候不能这么做, 但这里我们不需要考虑太多:
return max(len(x.strip()) for x in open('/etc/motd'))
在下一个核心笔记中, 我们将介绍如何使用 os 模块的一些属性来帮助你在不同平台下访问文件, 不同平台用来表示行结束的符号是不同的, 例如 \n, \r, 或者 \r\n . 所以, Python 的解释器也要处理这样的任务, 特别是在导入模块时分外重要。 你难道不希望 Python 用相同的方式处理所有文件吗?
这就是 UNS 的关键所在, 作为 PEP 278 的结果, Python 2.3 引入了 UNS. 当你使用 'U' 标志打开文件的时候, 所有的行分割符(或行结束符,无论它原来是什么)通过 Python 的输入方法(例
如 read*() )返回时都会被替换为换行符 NEWLINE(\n). ('rU' 模式也支持 'rb' 选项) . 这个特性还支持包含不同类型行结束符的文件. 文件对象的 newlines 属性会记录它曾“看到的”文件的行结束符
read() 方法用来直接读取字节到字符串中, 最多读取给定数目个字节. 如果没有给定 size
参数(默认值为 -1)或者 size 值为负, 文件将被读取直至末尾. 未来的某个版本可能会删除此方
法.
readline() 方法读取打开文件的一行(读取下个行结束符之前的所有字节). 然后整行,包括行结束符,作为字符串返回. 和 read() 相同, 它也有一个可选的 size 参数, 默认为 -1, 代表读至行结束符. 如果提供了该参数, 那么在超过 size 个字节后会返回不完整的行.
readlines() 方法并不像其它两个输入方法一样返回一个字符串. 它会读取所有(剩余的)行然后把它们作为一个字符串列表返回. 它的可选参数 sizhint 代表返回的最大字节大小. 如果它大于 0 , 那么返回的所有行应该大约有 sizhint 字节(可能稍微大于这个数字, 因为需要凑齐缓冲区大小)
write() 内建方法功能与 read() 和 readline() 相反.它把含有文本数据或二进制数据块的字符串写入到文件中去.
和 readlines() 一样,writelines() 方法是针对列表的操作,它接受一个字符串列表作为参数 , 将它们写入文件 . 行结 束符并不会自动加入 ,所以如果需要的话 ,你必须在调用writelines()前给每行结尾加上行结束符.
注意这里并没有 "writeline()" 方法, 因为它等价于使用以行结束符结尾的单行字符串调用
write() 方法
你可以使用 try-except 语句检测和处理异常. 你也可以添加一个可选的 else 子句处理没有探测到异常的时执行的代码. 而 try-finally 只允许检测异常并做一些必要的清除工作(无论发生错误与否), 没有任何异常处理设施. 正如你想像的,复合语句两者都可以做到.
try:f = open('blah', 'r')except IOError, e:print 'could not open file:', e
>>> float(['this is', 1, 'list']) Traceback (innermost last):File "<stdin>", line 1, in ?float(['this is', 1, 'list'])TypeError: float() argument must be a string or a number
从上面的错误我们可以看出, float() 对不合法的参数很不客气. 例如, 如果参数的类型正确(字符串), 但值不可转换为浮点数, 那么将引发 ValueError 异常, 因为这是值的错误. 列表也是不合法的参数, 因为他的类型不正确, 所以, 引发一个 TypeError 异常
我们的目标是"安全地"调用 float() 函数, 或是使用一个"安全的方式" 忽略掉错误, 因为它们与我们转换数值类型的目标没有任何联系,而且这些错误也没有严重到要让解释器终止执行. 为了实现我们的目的,这里我们创建了一个"封装"函数, 在 try-except的协助下创建我们预想的环境, 我们把他叫做 safe_float() . 在第一次改进中我们搜索并忽略 ValueError , 因为这是最常发生的. 而 TypeError 并不常见, 我们一般不会把非字符串数据传递给 float().
def safe_float(obj):try:return float(obj)except ValueError:pass
我们采取的第一步只是"止血". 在上面的例子中, 我们把错误"吞了 下去". 换句话说, 错误会被探测到, 而我们在 except 从句里没有放任何东西(除了一个 pass , 这是为了语法上的需要.),不进行任何处理, 忽略这个错误.
优化好的代码:
def safe_float(obj):try:retval = float(obj)except ValueError:retval = 'could not convert non-number to float'return retval
try:float(['float() does not', 'like lists', 2])except TypeError, diag:# capture diagnostic infopasstype(diag)print diag
我们首先在一个 try 语句块中引发一个异常,随后简单的忽略了这个异常,但保留了错误的信息。调用内置的 type()函数,我们可以确认我们的异常对象的确是 TypeError 异常类的实例。最后我们对异常诊断参数调用 print 以显示错误。
我们已经看过 else 语句段配合其他的 Python 语句,比如条件和循环.至于 try-except 语句段,它的功能和你所见过的其他 else 没有太多的不同:在 try 范围中没有异常被检测到时,执行 else 子句.
在 else 范围中的任何代码运行前,try范围中的所有代码必须完全成功(也就是,结束前没有引发异常).下面是用 Python 伪代码写的简短例子
import 3rd_party_modulelog = open('logfile.txt', 'w')try:3rd_party_module.function()except:log.write("*** caught exception in module\n")else:log.write("*** no exceptions caught\n")log.close()
在前面的例子中,我们导入了一个外部的模块然后测试是否有错误.用一个日志文件来确定这个第三方模块是有无缺陷.根据运行时是否引发异常,我们将在日志中写入不同的消息。
finally 子句是无论异常是否发生,是否捕捉都会执行的一段代码.你可以将 finally 仅仅配合try 一起使用,也可以和 try-except(else也是可选的)一起使用.独立的 try-finally 将会在下一章
介绍,我们稍后再来研究
下面是 try-except-else-finally 语法的示例:
try:Aexcept MyException:Belse:Cfinally:D
当然,无论如何,你都可以有不止一个的 except 子句,但最少有一个 except 语句,而 else 和finally 都是可选的.A,B,C 和 D是程序(代码块).程序会按预期的顺序执行.(注意:可能的顺序是A-C-D[正常]或 A-B-D[异常]).无论异常发生在 A,B,和/或 C 都将执行finally块.旧式写法依然有效,所以没有向后兼容的问题.
with open('/etc/passwd', 'r') as f:for eachLine in f:# ...do stuff with eachLine or f...
这个代码片段干了什么呢...嗯,这是Python,因而你很可能的已经猜到了.它会完成准备工作,比如试图打开一个文件,如果一切正常,把文件对象赋值给f.然后用迭代器遍历文件中的每一行,当完成时,关闭文件.无论的在这一段代码的开始,中间,还是结束时发生异常,会执行清理的代码,此外文件仍会被自动的关闭.