我爱编程

python核心编程学习笔记(6-10)

2018-03-23  本文已影响0人  m风满楼

第六章:数字

第七章:序列,列表,元组

1.序列

序列类型有着相同的访问模式:它的每一个元素可以通过指定一个偏移量的方式得到。而
多个元素可以通过切片操作的方式一次得到

标准类型操作符

>, < .== 值比较

is ,is not 身份比较

and or 布尔逻辑运算符

序列类型操作符
  1. 成员关机操作符
    in ,not in
  2. 序列类型操作符

序列操作符 --------作用

seq[ind] --------获得下标为 ind 的元素

seq[ind1:ind2]----获得下标从 ind1 到 ind2 间的元素集合

seq * expr --------序列重复 expr 次

seq1 + seq2 ------连接序列 seq1 和 seq2

obj in seq --------判断 obj 元素是否包含在 seq 中

obj not in seq ----判断 obj 元素是否不包含在 seq 中

  1. 连接接操作符(+)

把一个序列和另一个相同类型的序列做连接

这个操作不是最快或者说最有效的。对字符
串来说,这个操作不如把所有的==子字符串放到一个列表或可迭代对象中==,然后调用一个 join
方法来把所有的内容连接在一起节约内存;类似地,对列表来说,我们推荐读者用==列表类型的
extend()方法来把两个或者多个列表对象合并==.当你需要简单地把两个对象的内容合并, 或者说
不能依赖于可变对象的那些没有返回值(实际上它返回一个 None)的内建方法来完成的时候时,
连接操作符还是很方便的一个选择。

a = 'abc'
b = 'cde'
c = ''.join([a,b])
  1. 重复操作符 ( * )

当你需要需要一个序列的多份拷贝时,重复操作符非常有用,

  1. 切片操作符 ( [], [:], [::] )

简单地讲, 所谓序列类型就是包含一些顺序排列的对象的一个结构.你可以简单的用方括号
加一个下标的方式访问它的每一个元素,或者通过在方括号中用冒号把开始下标和结束下标分
开的方式来访问一组连续的元素.下面我们将详细的讲解提到的这两种方式.==序列类型是其元素
被顺序放置的一种数据结构类型==

切片操作的三种形式:

s = 'abcdefghigklmn'
a = s[:2]       ab
b = s[0:2]      ab
c = s[2:]       cdefghigklmn
c = s[0:10:3]   adgg
内建函数(BIFS)
  1. 类型转换

==转换实际上是工厂函数,将对象作为参数,并将其内容(浅)拷贝到新生成的对象中==

函数 含义

list(iter) -------把可迭代对象转换为列表

str(obj) -------把 obj
对象转换成字符串(对象的字符串表示法)
unicode(obj) --把对象转换成 Unicode 字符串(使用默认编码)

basestring() ----抽象工厂函数,其作用仅仅是为 str 和 unicode 函数提供父类,所以不能被
实例化,也不能被调用(详见第 6.2 节)

tuple(iter) -------把一个可迭代对象转换成一个元组对象

为什么 Python 里面不简单地把一个对象转换成另

一个对象呢?

回过头看一下第 4 章就会知道,一旦一个 Python 的对象被建立,我们就不能更改
其身份或类型了.如果你把一个列表对象传给 list()函数,便会创建这个对象的一个浅拷贝,
然后将其插入新的列表中。同样地,在做连接操作和重复操作时,我们也会这样处理

python 内建对象汇总

==函数名==                            功能
enumerate(iter) a                接受一个可迭代对象作为参数,返回一个 enumerate 对象(同时也是一个迭代器),该对象生成由 iter 每个元素的index 值和 item 值组成的元组(PEP 279)
len(seq)                         返回 seq 的长度
max(iter,key=None) or
max(arg0,arg1...,key=None)b      返回 iter 或(arg0,arg1,...)中的最大值,如果指定了 key,这个 key 必须是一个可以传给 sort()方法的,用于比较的回调函数.
min(iter, key=None) or
min(arg0, arg1.... key=None)b    返回 iter 里面的最小值;或者返回(arg0,arg2,...)里面的最小值;如果指定了 key,这个 key 必须是一个可以传给sort()方法的,用于比较的回调函数.
reversed(seq)c                   接受一个序列作为参数,返回一个以逆序访问的迭代器(PEP 322)
sorted(iter,func=None,key=None,reverse=False)c         接受一个可迭代对象作为参数,返回一个有序的列表;可选参数func,key 和 reverse 的含义跟 list.sort()内建函数的参数含义一样.
sum(seq, init=0)a                返 回 seq 和 可 选 参 数 init 的 总 和 , 其 效 果 等 同 于reduce(operator.add,seq,init)
zip([it0, it1,... itN])d         返回一个列表,其第一个元素是it0,it1,...这些元素的第一个元素组成的一个元组,第二个...,类推.

实例:

lambda 函数(自带return)

f = lambda x: x + 1
    f(2)

filter(func, list)

接受两个参数:一个函数func和一个列表list,返回一个列表。函数func只能有一个参数。列表中所有元素作为参数传递给函数,返回可以另func返回真的元素的列表

a = filter(lambda x: str(x).startswith('a'),['aaa','abd','bdc','rgt'])
    print(list(a))

zip(iter1,iter2,iter3,...)

zip函数接受任意多个序列作为参数,将所有序列按相同的索引组合成一个元素是各个序列合并成的tuple的新序列,新的序列的长度以参数中最短的序列为准。另外(*)操作符与zip函数配合可以实现与zip相反的功能,即将合并的序列拆成多个tuple

a = zip([1,2,3], ['a', 'b', 'c'])
print(list(a))              [(1, 'a'), (2, 'b'), (3, 'c')]

map(func,iter1)

为list中每个对象执行func,绑定的函数为修改list中每一个值的函数

a = map(lambda x:x*2,[1,2,3,4])     #<map object at 0x101ed4710>
print(list(a))                      [2, 4, 6, 8]

2.字符串

跟数字类型一样,字符串类型也是不可变的,所以你要改变一个字符串就必须通过创建一
个新串的方式来实现。也就是说你不能只改变一个字符串的一个字符或者一个子串,然而,通
过拼凑一个旧串的各个部分来得到一个新串是被允许的

3.字符串和操作符

字符串索引的时候,直接使用超过字符串长度的索引值会报错,但是在切片的时候,如果切片的值超过了字符串的长度,不会报错,表示从开始只到末尾值

a = 'abcd'
a[2:4]      #'cd'
a[4]        #IndexError: string index out of range

在进行反向索引操作时,是从-1 开始,向字符串的开始方向计数,到字符串长度的负数为
索引的结束


成员操作符(in ,not in) ##判断字符

成员操作符用于判断一个==字符==或者一个==子串中的字符==是否出现在另一个字符串中。出现
则返回 True,否则返回 False.注意,成员操作符不是用来==判断一个字符串是否包含另一个字符
串的,这样的功能由 find()或者 index()(还有它们的兄弟:rfind()和 rindex())函数来完成==

字符串连接(+)
  1. 运行时字符串连接
  2. 普通字符串转化为 Unicode 字符串
如果把一个普通字符串和一个 Unicode 字符串做连接处理,Python 会在连接操作前先把普
通字符串转化为 Unicode 字符串

'Hello' + u' ' + 'World' + u'!'     # u'Hello World!'
  1. 编译时字符串连接
只适用于字符串的操作符
  1. 格式化操作符( % )

Python 支持两种格式的输入参数。第一种是元组(见 2.8 节,6.15 节),这基本上是一种的 C
printf()风格的转换参数集;
Python 支持的第二种形式是字典形式(详见第 7 章).字典其实是一个哈希键-值对的集合。
这种形式里面,key 是作为格式字符串出现,相对应的 value 值作为参数在进行转化时提
供给格式字符串

  1. 字符串模版

  2. 原始字符串操作符(r/R)

关于原始字符串的目的,在 Python1.5 里面已经有说明,是为了对付那些在字符串中出现
的特殊字符(下面的小节会介绍这些特殊字符)。在原始字符串里,所有的字符都是直接按照字
面的意思来使用,没有转义特殊或不能打印的字符。

  1. Unicode 字符串操作符( u/U )

Unocide 字符串操作符,大写的(U)和小写的(u)是在 Python1.6 中 和 Unicode 字符串一
起被引入的. 它用来把标准字符串或者是包含 Unicode 字符的字符串转换成完全地 Unicode 字
符串对象。

内建函数
  1. 标准类型函数
  2. 序列类型函数
  3. 字符串类型函数

raw_input()

内建的 raw_input()函数使用给定字符串提示用户输入并将这个输入返回,

str() and unicode()

str()和 unicode()函数都是工厂函数,就是说产生所对应的类型的对象.它们接受一个任
意类型的对象, 然后创建该对象的可打印的或者 Unicode 的字符串表示.

chr(), unichr(), and ord()

chr()函数用一个范围在 range(256)内的(就是 0 到 255)整数做参数,返回一个对应的字
符.unichr()跟它一样,只不过返回的是 Unicode 字符,这个从 Python2.0 才加入的 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 的异常

字符串内建函数
string.decode(encoding='UTF-8',errors='strict')            以 encoding 指定的编码格式解码 string,如果出错默认报一个ValueError 的 异 常 , 除 非 errors 指 定的 是 'ignore' 或 者'replace'

string.encode(encoding='UTF-8',errors='strict')            以 encoding指定的编码格式编码 string,如果出错默认报一个ValueError的异常,除非errors指定的是'ignore'或者'replace
字符串的独特特性
  1. 特殊字符串和控制字符

像其他高级语言和脚本语言一样, 一个反斜线加一个单一字符可以表示一个特殊字符,通常
是一个不可打印的字符,这就是我们上面讨论的特殊字符,如果这些特殊字符是包含在一个原
始字符串中的,那么它就失去了转义的功能

  1. 三引号
  2. 字符串不变性

每次你修改一个字符串或者做一些改变字符串内容的操作时,Python 都会自动为你分配
一个新串

Unicode

从 Python1.6 起引进的Unicode字符串支持,是用来在多种双字节字符的格式、 编码进行转换的,其中包括一些对这类字符串的操作管理功能。 内建的字符串和正则表达式对 Unicode 字符串的支持,再加上 string 模块的辅助,Python 已经可以应付大部分应用对 Unicode 的存储、访问、操作的需要了。

Unicode 通过使用一个或多个字节来表示一个字符的方法突破了 ASCII 的限制. 在这样机
制下, Unicode 可以表示超过 90,000 个字符

为了让 Unicode 和 ASCII 码值的字符串看起来尽可能的相像,Python 的字符串从原来的简
单数据类型改成了真正的对象.ASCII 字符串成了 StringType,而 Unicode 字符串成了
UnicodeType 类型.它们的行为是非常相近的.string 模块里面都有相应的处理函数.string 模
块已经停止了更新,只保留了 ASCII 码的支持,string 模块已经不推荐使用,在任何需要跟
Unicode 兼容的代码里都不要再用该模块,Python 保留该模块仅仅是为了向后兼容

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 字符串==.

codec 是 COder/DECoder 的首字母组合.它定义了文本跟二进制值的转换方式,跟 ASCII 那
种用一个字节把字符转换成数字的方式不同,Unicode 用的是多字节.这导致了 Unicode 支持多
种 不 同 的 编 码 方 式 . 比 如 说 codec 支 持 的 四 种 耳 熟 能 详 的 编 码 方 式 是 :ASCII,ISO
8859-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 已经替我们完成了相
关细节,我们无须为处理多字节字符的复杂问题而担心.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实际应用

程序中出现字符串时一定要加个前缀 u.

字符串关键点总结

列表

  1. 标准类型函数
  2. 序列类型函数
    list(),tuple()

list()函数和 tuple()函数接受可迭代对象(比如另一个序列)作为参数,并通过浅拷贝数据
来创建一个新的列表或者元组.虽然字符串也是序列类型的,但是它们并不是经常用于 list()和
tuple(). 1

无论 list()还是 tuple()都不可能做完全的转换(见
6.1.2 节).也就是说,你传给 tuple()的一个列表对象不可能变成一个元组,而你传给 list()的
对象也不可能真正的变成一个列表.虽然前后两个对象(原来的和新的对象)有着相同的数据集
合(所以相等 == ),但是变量指向的却不是同一个对象了(所以执行 is 操作会返回 false).还
要注意,即使它们的所有的值都相同,一个列表也不可能"等于"一个元组

  1. 列表类型内建函数

那些可以改变对象值的可变对象的方法是没有返回值的!
Python 初学者经常会陷入一个误区:调用一个方法就返回一个值.最明显的例子就是

sort():
>>> music_media.sort()# 没有输出?
>>>

在使用可变对象的方法如 sort(),extend()和 reverse()的时候要注意,这些操作会在列表
中原地执行操作,也就是说现有的列表内容会被改变,但是没有返回值!是的,与之相反,字符串,方法确实有返回值:

>>> 'leanna, silly girl!'.upper()
'LEANNA, SILLY GIRL!'

温习一下,字符串是不可变的 -- 不可变对象的方法是不能改变它们的值的,所以它们必须
返回一个新的对象.如果你确实需要返回一个对象,那么我们建议你看一下 Python2.4 以后加入的 reversed()和 sorted()内建函数.
它们像列表的方法一样工作,不同的是它们可以用做表达式,因为它们返回一对象.同时原来的那个列表还是那个列表,没有改变,而你得到的是一个新的对象.

列表的特殊特性(用列表构建其他数据结构)

堆栈

元组

元组是一种
不可变类型.正因为这个原因,元组能做一些列表不能做的事情... 用做一个字典的 key.另外当
处理一组对象时,这个组默认是元组类型.

元组的特殊特性

不可变性给元组带来了什么影响?

在三个标准不可变类型里面--数字, 字符串和元组字符串--元组是受到影响最大的,一个数
据类型是不可变的,简单来讲,就意味着一旦一个对象被定义了,它的值就不能再被更新,除非重
新创建一个新的对象.对数字和字符串的影响不是很大,因为它们是标量类型,当它们代表的值
改变时,这种结果是有意义的,是按照你所想要的方式进行访问的,而对于元组,事情就不是
这样了。
因为元组是容器对象,很多时候你想改变的只是这个容器中的一个或者多个元素,不幸的
是这是不可能的,切片操作符不能用作左值进行赋值。这和字符串没什么不同,切片操作只能
用于只读的操作。

元组也不是那么“不可变”

可以将几个小元祖组成一个大的元祖,连接操作可用

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']
['abc']
>>> type(['abc'
<type 'list'>
>>>
>>> ('xyz')
'xyz'
>>> type(('xyz'
<type 'str'>

圆括号被重载了,它也被用作分组操作符。由圆括号包裹的一个单一元素首
先被作为分组操作,而不是作为元组的分界符。一个变通的方法是在第一个元素后面添一个逗
号(,)来表明这是一个元组而不是在做分组操作

核心笔记:列表 VS 元组(为什么我们要区分元组和列表变量?)
这个问题也可以被表述为“我们真的需要两个相似的序列类型吗?”,一个原因是在有些情况下,使用其中的一种类
型要优于使用另一种类型。
最好使用不可变类型变量的一个情况是,如果你在维护一些敏感的数据,并且需要把这些
数据传递给一个并不了解的函数(或许是一个根本不是你写的 API),作为一个只负责一个软件
某一部分的工程师,如果你确信你的数据不会被调用的函数篡改,你会觉得安全了许多。
一个需要可变类型参数的例子是,如果你在管理动态数据集合时。你需要先把它们创建出
来,逐渐地或者不定期的添加它们,或者有时还要移除一些单个的元素。这是一个必须使用可
变类型对象的典型例子。幸运的是,通过内建的 list()和 tuple()转换函数,你可以非常轻松
的在两者之间进行转换.
list()和 tuple()函数允许你用一个列表来创建一个元组,反之亦然.如果你有一个元组变
量,但你需要一个列表变量因为你要更新一下它的对象,这时 list()函数就是你最好的帮手.如
果你有一个列表变量,并且想把它传递给一个函数,或许一个 API,而你又不想让任何人弄乱你
的数据,这时 tuple()函数就非常有用

字典的关键字

==不可变对象的值是不可改变的。这就意味着它们通过 hash 算法得到的值总是一个值。这是作为字典键值的一个必备条件==

拷贝 Python 对象浅拷贝和深拷贝

>>> 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]

>>> 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'了吗?为什么名字没有变成一样的呢?怎么会是这样呢?这是因为
在这两个列表的两个对象中,==第一个对象是不可变的(是个字符串类型),而第二个是可变的(一
个列表).正因为如此,当进行浅拷贝时,字符串被显式的拷贝,并新创建了一个字符串对象,而列
表元素只是把它的引用复制了一下,并不是它的成员==.所以改变名字没有任何问题,但是更改他
们银行账号的任何信息都会引发问题.现在,让我们分别看一下每个列表的元素的对象 ID 值,注
意,银行账号对象是同一个对象,这也是为什么对一个对象进行修改会影响到另一个的原因.注
意在我们改变他们的名字后,新的名字字符串是如何替换原有'名字'字符串

==几点关于拷贝操作的警告。==

第一,非容器类型(比如数字,字符串和其他"原子"类型的
对象,像代码,类型和 xrange 对象等)没有被拷贝一说,浅拷贝是用完全切片操作来完成的.第二,
如果元组变量只包含原子类型对象,对它的深拷贝将不会进行.

浅拷贝的时候可变类型的也只拷贝引用,不可变类型新建

序列类型小结

第七章:映象和集合类型

1:映射类型:字典

核心笔记:什么是哈希表?它们与字典的关系是什么?

序列类型用有序的数字键做索引将数据以数组的形式存储。 一般,索引值与所存储的数据毫无
关系。还可以用另一种方式来存储数据:基于某种相关值, 比如说一个字符串。
哈希表是一种数据结构:它按照我们所要求的去工作。哈希表中存储的每一条数据,叫做一个
值(value),是根据与它相关的一个被称作为键(key)的数据项进行存储的。键和值合在一起被称为
“键-值 对”(key-value pairs)。 哈希表的算法是获取键,对键执行一个叫做哈希函数的操作,
并根据计算的结果,选择在数据结构的某个地址中来存储你的值。任何一个值存储的地址皆取决于
它的键。正因为这种随意性,哈希表中的值是没有顺序的。你拥有的是一个无序的数据集。
你所能获得的有序集合只能是字典中的键的集合或者值的集合。方法 Keys() 或 values() 返回
一个列表,该列表是可排序的。 你还可以用 items()方法得到包含键、值对的元组的列表来排序。
由于字典本身是哈希的,所以是无序的。
哈希表一般有很好的性能, 因为用键查询相当快。

如何创建字典和给字典赋值
方法列表

2.映射类型操作符

7.4 映射类型内建方法

>>> dict2= {'host':'earth', 'port':80}
>>> dict3= {'host':'venus', 'server':'http'}
>>> dict2.update(dict3)
>>> dict2
{'server': 'http', 'port': 80, 'host': 'venus'}
>>> dict3.clear()
>>> dict3

>>> 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)]

7.5 字典的键

7.5.1 不允许一个键对应多个值
7.5.2 键必须是可哈希的
为什么键必须是可哈希的?

解释器调用哈希函数,根据字典中键的值来计算存储你的数据的位
置。如果键是可变对象,它的值可改变。如果键发生变化,哈希函数会映射到不同的地址来存储数
据。如果这样的情况发生,哈希函数就不可能可靠地存储或获取相关的数据。选择可哈希的键的原
因就是因为它们的值不能改变

7.6 集合类型

集合对象是一组无序排列的可哈希的值。
是的,集合成员可以做字典中的键。数学集合转为 Python 的集合对象很有效,集合关系测试和 union、
intersection 等操作符在 Python 里也同样如我们所预想地那样工作

集合(sets)有两种不同的类型,可变集合(set) 和 不可变集合(frozenset)。如你所想,对可
变集合(set),你可以添加和删除元素,对 不可变集合(frozenset)则不允许这样做。请注意,可变
集合(set)不是可哈希的,因此既不能用做字典的键也不能做其他集合中的元素。不可变集合
(frozenset)则正好相反,即,他们有哈希值,能被用做字典的键或是作为集合中的一个成员

八:循环和条件

8.11 迭代器和 iter() 函数

迭代器是在版本 2.2 被加入 Python 的, 它为类序列对象提供了一个类序列的接口. 我们在
前边的第 6 章已经正式地介绍过序列. 它们是一组数据结构,你可以利用它们的索引从 0 开始一直
"迭代" 到序列的最后一个条目

迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数. 当你或是一个循
环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它. 条目全部取
出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成

为什么要迭代器?

援引 PEP (234) 中对迭代器的定义:

如何迭代

根本上说, 迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数. 当你或是一个循
环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它. 条目全部取
出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.
不过, 迭代器也有一些限制. 例如你不能向后移动, 不能回到开始, 也不能复制一个迭代器.
如果你要再次(或者是同时)迭代同个对象, 你只能去创建另一个迭代器对象. 不过, 这并不糟糕,因为还有其他的工具来帮助你使用迭代器.

reversed() 内建函数将返回一个反序访问的迭代器. enumerate() 内建函数同样也返回迭代器.
另外两个新的内建函数, any() 和 all() , 在 Python 2.5 中新增, 如果迭代器中某个/所有条目
的值都为布尔真时,则它们返回值为真. 本章先前部分我们展示了如何在 for 循环中通过索引或是
可迭代对象来遍历条目. 同时 Python 还提供了一整个 itertools 模块, 它包含各种有用的迭代
器.

使用迭代器

===序列===

>>> myTuple = (123, 'xyz', 45.67)
>>> i = iter(myTuple)
>>> i.next()
123

for 循环会自动调用迭代器的 next() 方法(以及监视StopIteration 异常).

==字典==

字典和文件是另外两个可迭代的 Python 数据类型. 字典的迭代器会遍历它的键(keys).
语句 for eachKey in myDict.keys() 可以缩写为 for eachKey in myDict

==文件==
文件对象生成的迭代器会自动调用 readline() 方法. 这样, 循环就可以访问文本文件的所有行 . 程 序 员 可 以 使 用 更 简 单 的 for eachLine in myFile 替 换 for eachLine in myFile.readlines() :

>>> myFile = open('config-win.txt')
>>> for eachLine in myFile:
... print eachLine, # comma suppresses extra \n
...
[EditorWindow]
font-name: courier new
font-size: 10
>>> myFile.close()

列表解析

>>> [(x+1,y+1) for x in range(3) for y in range(5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2,
3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]

8.13 生成器表达式

列表解析的一个不足就是必要生成所有的数据, 用以创建整个列表. 这可能对有大量数据的迭
代器有负面效应. 生成器表达式通过结合列表解析和生成器解决了这个问题

生成器表达式在 Python 2.4 被引入, 它与列表解析非常相似,而且它们的基本语法基本相同;
不过它并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这
个条目“产生” (yield)出来. 生成器表达式使用了"延迟计算"(lazy evaluation), 所以它在使用内存上更有效

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
...
(1, 56)
(1, 2)
(1, 1)
(2, 56)
(2, 2)
(2, 1)
(3, 56)
(3, 2)
(3, 1)
(17, 56)
(17, 2)
(17, 1)

如何一步步使用生成器表达式(重要,文件打开的时候不要用readlines,会一次将所有的内容都打开)

=== 重构样例 ===
我们通过一个寻找文件最长的行的例子来看看如何改进代码. 在以前, 我们这样读取文件:

f = open('/etc/motd', 'r')
longest = 0
while True:
    if not linelen:
         break
    if linelen > longest:
         longest = linelen
f.close()
    return longest
    
事实上, 这还不够老. 真正的旧版本 Python 代码中, 布尔常量应该写是整数 1 , 而且我们应
该使用 string 模块而不是字符串的 strip() 方法:
import string
len(string.strip(f.readline()))
从那时起, 我们认识到如果读取了所有的行, 那么应该尽早释放文件资源. 如果这是一个很多
进程都要用到的日志文件, 那么理所当然我们不能一直拿着它的句柄不释放. 是的, 我们的例子是
用来展示的, 但是你应该得到这个理念. 所以读取文件的行的首选方法应该是这样:

f = open('/etc/motd', 'r')
longest = 0
allLines = f.readlines()
f.close()
for line in allLines:
    linelen = len(line.strip())
    if linelen > longest:
        longest = linelen
return longest

列表解析允许我们稍微简化我们代码, 而且我们可以在得到行的集合前做一定的处理. 在下段
代码中, 除了读取文件中的行之外,我们还调用了字符串的 strip() 方法处理行内容.
f = open('/etc/motd', 'r')
longest = 0
allLines = [x.strip() for x in f.readlines()]
f.close()
for line in allLines:
linelen = len(line)
if linelen > longest:
longest = linelen
return longest
 
然而, 两个例子在处理大文件时候都有问题, 因为 readlines() 会读取文件的所有行. 后来
我们有了迭代器, 文件本身就成为了它自己的迭代器, 不需要调用 readlines() 函数. 我们已经
做到了这一步, 为什么不去直接获得行长度的集合呢(之前我们得到的是行的集合)? 这样, 我们就
可以使用 max() 内建函数得到最长的字符串长度:

f = open('/etc/motd', 'r')
allLineLens = [len(x.strip()) for x in f]
f.close()
return max(allLineLens)

这里唯一的问题就是你一行一行迭代 f 的时候, 列表解析需要文件的所有行读取到内存中,
然后生成列表. 我们可以进一步简化代码: 使用生成器表达式替换列表解析, 然后把它移到 max()
函数里, 这样, 所有的核心部分只有一行:

f = open('/etc/motd', 'r')
longest = max(len(x.strip()) for x in f)
f.close()
return longest

最后, 我们可以去掉文件打开模式(默认为读取), 然后让 Python 去处理打开的文件. 当然,
文件用于写入的时候不能这么做, 但这里我们不需要考虑太多:

return max(len(x.strip()) for x in open('/etc/motd')

九:文件和输入输出

9.1文件对象

文件对象不仅可以用来访问普通的磁盘文件, 而且也可以访问任何其它类型抽象层面上的"文
件". 一旦设置了合适的"钩子", 你就可以访问具有文件类型接口的其它对象, 就好像访问的是普通文件一样

内建函数 open() 返回一个文件对象(参见下一小节), 对该文件进行后继相关的操作都要用到
它. 还有大量的函数也会返回文件对象或是类文件( file-like )对象. 进行这种抽象处理的主要原
因是许多的输入/输出数据结构更趋向于使用通用的接口. 这样就可以在程序行为和实现上保持一
致性. 甚至像 Unix 这样的操作系统把文件作为通信的底层架构接口. 请记住, 文件只是连续的字
节序列. 数据的传输经常会用到字节流, 无论字节流是由单个字节还是大块数据组成.

open() 的基本语法是:

file_object = open(file_name, access_mode='r', buffering=-1)
file_name 是包含要打开的文件名字的字符串, 它可以是相对路径或者绝对路径. 可选变量
access_mode 也是一个字符串, 代表文件打开的模式. 通常, 文件使用模式 'r', 'w', 或是 'a'
模式来打开, 分别代表读取, 写入和追加. 还有个 'U' 模式, 代表通用换行符支持(见下).

使用 'r' 或 'U' 模式打开的文件必须是已经存在的. 使用 'w' 模式打开的文件若存在则首
先清空, 然后(重新)创建. 以 'a' 模式打开的文件是为追加数据作准备的, 所有写入的数据都将
追加到文件的末尾. 即使你 seek 到了其它的地方. 如果文件不存在, 将被自动创建, 类似以 'w'
模式打开文件.

open() 和 file() 函数具有相同的功能, 可以任意替换

通用换行符支持(UNS)

在下一个核心笔记中, 我们将介绍如何使用 os 模块的一些属性来帮助你在不同平台下访问文
件, 不同平台用来表示行结束的符号是不同的, 例如 \n, \r, 或者 \r\n . 所以, Python 的解释器也要处理这样的任务, 特别是在导入模块时分外重要。 你难道不希望 Python 用相同的方式处理
所有文件吗?

这就是 UNS 的关键所在, 作为 PEP 278 的结果, Python 2.3 引入了 UNS. 当你使用 'U' 标志
打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例
如 read*() )返回时都会被替换为换行符 NEWLINE(\n). ('rU' 模式也支持 'rb' 选项) . 这个特性
还支持包含不同类型行结束符的文件. 文件对象的 newlines 属性会记录它曾“看到的”文件的行结
束符.

如果文件刚被打开, 程序还没有遇到行结束符, 那么文件的 newlines 为 None .在第一行被读
取后, 它被设置为第一行的结束符. 如果遇到其它类型的行结束符, 文件的 newlines 会成为一个
包含每种格式的元组. 注意 UNS 只用于读取文本文件. 没有对应的处理文件输出的方法.
在编译 Python 的时候,UNS 默认是打开的. 如果你不需要这个特性, 在运行 configure 脚本
时,你可以使用 --without-universal-newlines 开关关闭它. 如果你非要自己处理行结束符, 请
查阅核心笔记,使用 os 模块的相关属性.

9.3文件内建方法

==输入==

==输出==

==文件内移动==

==文件迭代==

read()方法和for循环一样可以迭代文件

在 Python 2.2 之前, 从文件中读取行的最好办法是使用 file.readlines() 来读取所有数据,
这样程序员可以尽快释放文件资源. 如果不需要这样做, 那么程序员可以调用 file.readline()
一次读取一行. 曾有一段很短的时间, file.xreadlines() 是读取文件最高效的方法.

在 Python 2.2 中, 我们引进了迭代器和文件迭代, 这使得一切变得完全不同, 文件对象成为
了它们自己的迭代器, 这意味着用户不必调用 read*() 方法就可以在 for 循环中迭代文件的每一行.
另外我们也可以使用迭代器的 next 方法, file.next() 可以用来读取文件的下一行. 和其它迭代
器一样, Python 也会在所有行迭代完成后引发 StopIteration 异常.

所以请记得, 如果你见到这样的代码, 这是"完成事情的老方法", 你可以安全地删除对
readline() 的调用.
for eachLine in f.readline():
:

文件迭代更为高效, 而且写(和读)这样的 Python 代码更容易. 如果你是 Python 新人, 那
么请使用这些新特性, 不必担心它们过去是如何.

close() 通过关闭文件来结束对它的访问. Python 垃圾收集机制也会在文件对象的引用计数降至零的时候自动关闭文件.

行分隔符

操作系统间的差异之一是它们所支持的行分隔符不同.

Python 的 os 模块设计者已经帮我们想到了这些问题. os 模块有五个很
有用的属性如下

不管你使用的是什么平台, 只要你导入了 os 模块, 这些变量自动会被设置为正确的值, 减少
了你的麻烦

9.4 文件内建属性

文件对象除了方法之外,还有一些数据属性. 这些属性保存了文件对象相关的附加数据, 例如
文件名(file.name ), 文件的打开模式 ( file.mode ), 文件是否已被关闭 ( file.closed), 以及
一 个 标 志 变 量 , 它 可 以 决 定 使 用 print 语 句 打 印 下 一 行 前 是 否 要 加 入 一 个 空 白 字 符
( file.softspace ).

9.5 标准文件

9.6 命令行参数

sys 模块通过 sys.argv 属性提供了对命令行参数的访问

9.7文件系统

对文件系统的访问大多通过 Python 的 os 模块实现. 该模块是 Python 访问操作系统功能的主
要接口. os 模块实际上只是真正加载的模块的前端, 而真正的那个"模块"明显要依赖与具体的操作
系统.

#!/usr/bin/env python
2
3 import os
Edit By Vheavens
Edit By Vheavens
4 for tmpdir in ('/tmp', r'c:\temp'):
5 if os.path.isdir(tmpdir):
6 break
7 else:
8 print 'no temp directory available'
9 tmpdir = ''
10
11 if tmpdir:
12 os.chdir(tmpdir)
13 cwd = os.getcwd()
14 print '*** current temporary directory'
15 print cwd
16
17 print '*** creating example directory...'
18 os.mkdir('example')
19 os.chdir('example')
20 cwd = os.getcwd()
21 print '*** new working directory:'
22 print cwd
23 print '*** original directory listing:'
24 print os.listdir(cwd)
25
26 print '*** creating test file...'
27 fobj = open('test', 'w')
28 fobj.write('foo\n')
29 fobj.write('bar\n')
30 fobj.close()
31 print '*** updated directory listing:'
32 print os.listdir(cwd)
33
34 print "*** renaming 'test' to 'filetest.txt'"
35 os.rename('test', 'filetest.txt')
36 print '*** updated directory listing:'
37 print os.listdir(cwd)
38
39 path = os.path.join(cwd, os.listdir (cwd)[0])
40 print '*** full file pathname'
41 print path
42 print '*** (pathname, basename) =='
43 print os.path.split(path)
 
44 print '*** (filename, extension) =='
45 print os.path.splitext(os.path.basename(path))
46
47 print '*** displaying file contents:'
48 fobj = open(path)
49 for eachLine in fobj:
50 print eachLine,
51 fobj.close()
52
53 print '*** deleting test file'
54 os.remove(path)
55 print '*** updated directory listing:'
56 print os.listdir(cwd)
57 os.chdir(os.pardir)
58 print '*** deleting test directory'
59 os.rmdir('example')
60 print '*** DONE'

9.9永久存储

==shelve 模块==

十:错误和异常

10.1 什么是异常

对异常的最好描述是: 它是因为程序出现了错误而在正常控制流以外采取的行为. 这个行为又
分为两个阶段: 首先是引起异常发生的错误,然后是检测(和采取可能的措施)阶段

10.2 Python 中的异常

10.3 检测和处理异常

核心笔记: 忽略代码, 继续执行, 和向上移交

try 语句块中异常发生点后的剩余语句永远不会到达(所以也永远不会执行). 一旦一个异常被
引发, 就必须决定控制流下一步到达的位置. 剩余代码将被忽略, 解释器将搜索处理器, 一旦找到,
就开始执行处理器中的代码.

如果没有找到合适的处理器, 那么异常就向上移交给调用者去处理, 这意味着堆栈框架立即回
到之前的那个.

如果在上层调用者也没找到对应处理器, 该异常会继续被向上移交, 直到找到合适处理器. 如果到达最顶层仍然没有找到对应处理器, 那么就认为这个异常是未处理的, Python 解释器会显示出跟踪返回消息, 然后退出.

核心笔记: 忽略代码, 继续执行, 和向上移交

try 语句块中异常发生点后的剩余语句永远不会到达(所以也永远不会执行). 一旦一个异常被引发, 就必须决定控制流下一步到达的位置. 剩余代码将被忽略, 解释器将搜索处理器, 一旦找到,
就开始执行处理器中的代码.如果没有找到合适的处理器, 那么异常就向上移交给调用者去处理, 这意味着堆栈框架立即回
到之前的那个.

如果在上层调用者也没找到对应处理器, 该异常会继续被向上移交, 直到找到合适
处理器. 如果到达最顶层仍然没有找到对应处理器, 那么就认为这个异常是未处理的, Python 解释
器会显示出跟踪返回消息, 然后退出.

10.3.6 异常参数

异常也可以有参数, 异常引发后它会被传递给异常处理器. 当异常被引发后参数是作为附加帮
助信息传递给异常处理器的. 虽然异常原因是可选的, 但标准内建异常提供至少一个参数, 指示异
常原因的一个字符串.

异常的参数可以在处理器里忽略, 但 Python 提供了保存这个值的语法. 我们已经在上边接触
到相关内容: 要想访问提供的异常原因, 你必须保留一个变量来保存这个参数. 把这个参数放在
except 语句后, 接在要处理的异常后面

else 子句

我们已经看过 else 语句段配合其他的 Python 语句,比如条件和循环.至于 try-except 语句段,
它的功能和你所见过的其他 else 没有太多的不同:在 try 范围中没有异常被检测到时,执行 else 子句.

在 else 范围中的任何代码运行前,try 范围中的所有代码必须完全成功(也就是,结束前没有引发
异常)

try-finally 语句

try-finally 语句和 try-except
区别在于它不是用来捕捉异常的.作为替代,它常常用来维持一致的行为而无论异常是否发生.我们得知无论 try 中是否有异常触发,finally 代码段都会被执行

10.4 上下文管理

10.4.1 with 语句

类似 try-except-finally , with 语句也是用来简化代码的,这与用 try-except 和 try-finally
所想达到的目的前后呼应.try-except 和 try-finally 的一种特定的配合用法是保证共享的资源的
唯一分配,并在任务结束的时候释放它.比如==文件(数据,日志,数据库等等),线程资源,简单同步,数据库连接==,等等. with 语句的目标就是应用在这种场景.

with 语句的目的在于从流程图中把 try,except 和 finally 关键字和资源分配释放相关
代码统统去掉, 而不是像 try-except-finally 那样仅仅简化代码使之易用. with 语法的基本用法
看上去如下:

with context_expr [as var]:
with_suite

*上下文管理协议

下面描述了一些关于协议如何和文件对象协同工作.让我们在此进一步地研究

上下文表达式(context_expr),上下文管理器

当 with 语句执行时,便执行上下文符号(译者注:就是 with 与 as 间内容)来获得一个上下文管理
器.上下文管理器的职责是提供一个上下文对象.这是通过调用context()方法来实现的.该方法
返回一个上下文对象,用于在 with 语句块中处理细节.有点需要注意的是上下文对象本身就可以是上
下文管理器.所以 context_expr 既可以为一个真正的上下文管理器,也可以是一个可以自我管理的上
下文对象.在后一种情况时,上下文对象仍然有context()方法,返回其自身,如你所想

上下文对象,with 语句块

一旦我们获得了上下文对象,就会调用它的enter()方法.它将完成 with 语句块执行前的所有准备工作.你可以注意到在上面的 with 行的语法中有一个可选的 as 声明变量跟随在 context_expr
之后.如果提供提供了变量,以enter()返回的内容来赋值;否则,丢弃返回值.

在我们的文件对象例子中,上下文对象的enter()返回文件对象并赋值给 f.现在,执行了 with 语句块.当 with 语句块执行结束,无论是"和谐地"还是由于异常,都会调用上
下文对象的exit()方法.exit()有三个参数.如果 with 语句块正常结束,三个参数全部是
None.如果发生异常,三个参数的值的分别等于调用 sys.exc_info()函数(见 10.12)返回的三个值:类
型(异常类),值(异常实例),和回溯(traceback),相应的回溯对象.

你可以自己决定如何在exit()里面处理异常.惯例是当你处理完异常时不返回任何值,或
返回 None,或返回其他布尔值为 False 对象.这样可以使异常抛给你的用户来处理.如果你明确的想
屏蔽这个异常,返回一个布尔为 True 的值.如果没有发生异常或你在处理异常后返回 True,程序会继
续执行 with 子句后的下一段代码.

因为==上下文管理器主要作用于共享资源==,你可以==想象到enter()exit()方法基本是干
的需要分配和释放资源的低层次工作,比如:
数据库连接,锁分配,信号量加减,状态管理,打开/关闭文件,异常处理,等等.
为 了 帮 助 你 编 写 对 象 的 上 下 文 管 理 器 , 有 一 个 contextlib 模 块 , 包 含 了 实 用 的
functions/decorators, 你 可 以 用 在 你 的 函 数 / 对 象 上 而 不 用 去 操 心 关 于 类 或
context(),enter(),enter(),exit()这些方法的实现==.

10.5 *字符串作为异常

10.6 触发异常

10.6.1 raise 语句

语法与惯用法

raise 语句对所支持是参数十分灵活,对应到语法上就是支持许多不同的格式.rasie 一般的用法是:

raise [SomeException [, args [, traceback]]]
最常见的用法为 SomeException 是一个类.不需要其他的参数,但如果有的话,可以是一个单一对
象参数,一个参数的元组,或一个异常类的实例.如果参数是一个实例,可以由给出的类及其派生类实
例化(已存在异常类的子集).若参数为实例,则不能有更多的其他参数.

当参数是一个实例的时候会发生什么呢? 该实例若是给定异常类的实例当然不会有问题, 然而,
如果该实例并非这个异常类或其子类的实例时, 那么解释器将使用该实例的异常参数创建一个给定
异常类的新实例. 如果该实例是给定异常类子类的实例, 那么新实例将作为异常类的子类出现, 而
不是原来的给定异常类

10.8 标准异常

下面所有的 Python 当前的标准异常集,所有的异常都是内建的. 所以它们在脚本启动前或在互交命令行提示符出现时已经是可用的了:

异常名称 描述
BaseExceptiona 所有异常的基类
SystemExitb python 解释器请求退出
KeyboardInterruptc 用户中断执行(通常是输入^C)
Exceptiond 常规错误的基类
StopIteratione 迭代器没有更多的值
GeneratorExita 生成器(generator)发生异常来通知退出
SystemExith Python 解释器请求退出
StandardErrorg 所有的内建标准异常的基类
ArithmeticErrord 所有数值计算错误的基类
FloatingPointErrord 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionErrord 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达 EOF 标记
EnvironmentErrord 操作系统错误的基类
IOError 输入/输出操作失败
OSErrord 操作系统错误
WindowsErrorh Windows 系统调用失败
ImportError 导入模块/对象失败
KeyboardInterruptf 用户中断执行(通常是输入^C)
LookupErrord 无效数据查询的基类
IndexError 序列中没有没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于 Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalErrorh 访问未初始化的本地变量
ReferenceErrore 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedErrord 尚未实现的方法
SyntaxError Python 语法错误
IndentationErrorg 缩进错误
TabErrorg Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeErrorh Unicode 相关的错误
UnicodeDecodeErrori Unicode 解码时的错误
UnicodeEncodeErrori Unicode 编码时错误
UnicodeTranslateErrorf Unicode 转换时错误
Warningj 警告的基类
DeprecationWarningj 关于被弃用的特征的警告
FutureWarningi 关于构造将来语义会有改变的警告
OverflowWarningk 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarningi 关于特性将会被废弃的警告
RuntimeWarningj 可疑的运行时行为(runtime behavior)的警告
SyntaxWarningj 可疑的语法的警告
UserWarningj 用户代码生成的警告

10.9 *创建异常

10.10 为什么用异常(现在)?

毫无疑问,错误的存在会伴随着软件的存在.区别在于当今快节奏的计算世界, 我们的执行环境
已经改变, 所以我们需要改变错误处理, 以准确反映我们软件的开发环境. 就现今应用来说, 普遍
的是自洽(self-contained)的图形用户界面(GUIs)或是客户机/服务器体系, 例如 Web.

在应用层处理错误的能力近来变得更为重要, 用户已不再是应用程序的的唯一的直接运行者.
随着互联网和网上电子商业应用越来越普及, web 服务器将成为应用软件的主要客户. 这意味着应用
程序再也不能只是直接的失败或崩溃, 因为如果这样, 系统错误导致浏览器的错误, 这反过来又
会让用户沮丧. 失去眼球意味着失去广告收入和和潜在的大量无可挽回的生意.

如果错误的确发生了, 它们一般都归因于用户输入的数据无效. 运行环境必须足够强健,来处
理应用级别的错误,并提供用户级别的错误信息.就服务器而言,这必须转化为一个"非错误" . 因为
应用必须要成功的完成, 即使所做的不过是返回一个错误的信息, 向用户是提供一个有效的超文本标记语言(HTML)的网页指明错误.

如果你不清楚我在说什么, 那个一个简单的网页浏览器窗口,用大而黑的字体写到"内部服务器
错误"是否更耳熟?用一个弹出式窗口宣告"文件中没有数据"的致命错误如何?作为一个用户, 这
些词语对你有意义吗?没有, 当然没有(除非你是一个互联网软件工程师), 至于对普通用户来说,
这些是无休止的混乱和挫折感的来源. 这些错误导致在执行的程序时的失败. 应用不论是返回无效
的超文本传输协议 ( http)数据还是致命地终止, 都会导致 Web 服务器举手投降, 说: "我放弃" !

这种类型的执行错误不应该被允许, 无论情况如何. 随着系统变得更加复杂, 又牵涉到更多的新手用户, 要采取额外的措施, 确保用户平滑的学到应用经验. 即使面对一个错误, 应用应该成功
的中止, 不至于灾难性的影响其执行环境. Python 异常处理促使成熟和正确的编程.

10.11 到底为什么要异常?

因为错误的数据需要在调用层次中向上转发,
但在前进的道路上可能被曲解. 一个不相干的错误可能会被宣布为起因,而实际上它与原始问题完
全无关.在一层一层的传递中,我们失去了对原始错误封装和保管的能力,更不用说完全地失去我们
原本关心的数据的踪影!异常不仅简化代码, 而且简化整个错误管理体系 --- 它不该在应用开发中
如此重要角色;而有了 Python 的异常处理能力, 也的确没有必要了

10.12 异常和 sys 模块

另一种获取异常信息的途径是通过 sys 模块中 exc_info()函数. 此功能提供了一个 3 元组(3-tuple)的信息, 多于我们单纯用异常参数所能获得.

上一篇下一篇

猜你喜欢

热点阅读