python补充.docx
《python补充.docx》由会员分享,可在线阅读,更多相关《python补充.docx(19页珍藏版)》请在冰豆网上搜索。
python补充
标准异常:
∙Exception 是所有异常的基类.强烈建议(但不是必须)自定义的异常异常也继承这个类.
∙SystemExit(Exception) 由 sys.exit 函数引发.如果它在最顶层没有被 try-except 语句捕获,那么解释器将直接关闭而不会显示任何跟踪返回信息.
∙StandardError(Exception) 是所有内建异常的基类(除 SystemExit 外).
∙KeyboardInterrupt(StandardError) 在用户按下Control-C(或其他打断按键)后被引发.如果它可能会在你使用"捕获所有"的 try-except 语句时导致奇怪的问题.
∙ImportError(StandardError) 在Python导入模块失败时被引发.
∙EnvironmentError 作为所有解释器环境引发异常的基类.(也就是说,这些异常一般不是由于程序bug引起).
∙IOError(EnvironmentError) 用于标记I/O相关错误.
∙OSError(EnvironmentError) 用于标记 os 模块引起的错误.
∙WindowsError(OSError) 用于标记 os 模块中Windows相关错误.
∙NameError(StandardError) 在Python查找全局或局部名称失败时被引发.
∙UnboundLocalError(NameError) ,当一个局部变量还没有赋值就被使用时,会引发这个异常.这个异常只有在2.0及之后的版本有;早期版本只会引发一个普通的 NameError .
∙AttributeError(StandardError) ,当Python寻找(或赋值)给一个实例属性,方法,模块功能或其它有效的命名失败时,会引发这个异常.
∙SyntaxError(StandardError) ,当解释器在编译时遇到语法错误,这个异常就被引发.
∙(2.0及以后版本) IndentationError(SyntaxError) 在遇到非法的缩进时被引发.该异常只用于2.0及以后版本,之前版本会引发一个 SyntaxError 异常.
∙(2.0及以后版本) TabError(IndentationError) ,当使用 -tt 选项检查不一致缩进时有可能被引发.该异常只用于2.0及以后版本,之前版本会引发一个SyntaxError 异常.
∙TypeError(StandardError) ,当给定类型的对象不支持一个操作时被引发.
∙AssertionError(StandardError) 在 assert 语句失败时被引发(即表达式为false时).
∙LookupError(StandardError) 作为序列或字典没有包含给定索引或键时所引发异常的基类.
∙IndexError(LookupError) ,当序列对象使用给定索引数索引失败时(不存在索引对应对象)引发该异常.
∙KeyError(LookupError) 当字典对象使用给定索引索引失败时(不存在索引对应对象)引发该异常.
∙ArithmeticError(StandardError) 作为数学计算相关异常的基类.
∙OverflowError(ArithmeticError) 在操作溢出时被引发(例如当一个整数太大,导致不能符合给定类型).
∙ZeroDivisionError(ArithmeticError) ,当你尝试用0除某个数时被引发.
∙FloatingPointError(ArithmeticError) ,当浮点数操作失败时被引发.
∙ValueError(StandardError) ,当一个参数类型正确但值不合法时被引发.
∙(2.0及以后版本) UnicodeError(ValueError) ,Unicode字符串类型相关异常.只使用在2.0及以后版本.
∙RuntimeError(StandardError) ,当出现运行时问题时引发,包括在限制模式下尝试访问外部内容,未知的硬件问题等等.
∙NotImplementedError(RuntimeError) ,用于标记未实现的函数,或无效的方法.
∙SystemError(StandardError) ,解释器内部错误.该异常值会包含更多的细节(经常会是一些深层次的东西,比如"eval_code2:
NULLglobals").这本书的作者编了5年程序都没见过这个错误.(想必是没有用 raiseSystemError).
∙MemoryError(StandardError) ,当解释器耗尽内存时会引发该异常.注意只有在底层内存分配抱怨时这个异常才会发生;如果是在你的旧机器上,这个异常发生之前系统会陷入混乱的内存交换中
编码转换
python有strobject和unicodeobject两种字符串,都可以存放字符的字节编码,但是他们是不同的type,这一点很重要,也是为什么会有encode和decode。
encode和decode在pyhton 中的意义可表示为
encode
unicode------------------------->str
unicode<--------------------------str
decode
几种常用法:
str_string.decode('codec')是把str_string转换为unicode_string,codec是源str_string的编码方式
unicode_string.encode('codec')是把unicode_string转换为str_string,codec是目标str_string的编码方式
str_string.decode('from_codec').encode('to_codec')可实现不同编码的str_string之间的转换
比如:
>>> t='长城'
>>> t
'\xb3\xa4\xb3\xc7'
>>> t.decode('gb2312').encode('utf-8')
'\xe9\x95\xbf\xe5\x9f\x8e'
str_string.encode('codec')是先调用系统的缺省codec去把str_string转换为unicode_string,然后用encode的参数codec去转换为最终的str_string.相当于str_string.decode('sys_codec').encode('codec')。
unicode_string.decode('codec')基本没有意义,unicode在python里只用一种unicode编码,UTF16或者UTF32(编译python时就已经确定),没有编码转换的需要。
注:
缺省codec在site-packages下的sitecustomize.py文件中指定,比如
import sys
sys.setdefaultencoding('utf-8')
5.2案例研究:
街道地址
下面一系列的示例的灵感来自于现实生活中我几年前每天的工作。
我需要把一些街道地址导入一个新的系统,在这之前我要从一个遗留的老系统中清理和标准化这些街道地址。
下面这个例子展示我怎么解决这个问题。
>>>s='100NORTHMAINROAD'
>>>s.replace('ROAD','RD.')①
'100NORTHMAINRD.'
>>>s='100NORTHBROADROAD'
>>>s.replace('ROAD','RD.')②
'100NORTHBRD.RD.'
>>>s[:
-4]+s[-4:
].replace('ROAD','RD.')③
'100NORTHBROADRD.'
>>>importre④
>>>re.sub('ROAD$','RD.',s)⑤
'100NORTHBROADRD.'
1.我的目的是要标准化街道的格式。
而‘ROAD’总是在.RD的前面。
刚开始我以为只需要简单的使用string的replace()方法就可以。
所有的数据都是大写的,因此不会出现大小写不匹配的问题。
而查找的字符串‘ROAD’也是一个常量。
在这个简单的例子中s.replace()可以很好的工作。
2.事实上,不幸的是,我很快发现一个问题,在一些地址中‘ROAD’出现了两次,一个是前面的街道名里带了‘ROAD’,一个是‘ROAD’本身。
repalce()发现了两个就把他们都给替换掉了。
这意味着,我的地址错了。
3.为了解决地址中出现超过一个‘ROAD’子字符串的问题,你可能会这么考虑:
只在地址的最后四个字符中查找和替换‘‘ROAD’(s[-4:
])。
然后把剩下的字符串独立开来处理(s[:
-4])。
这个方法很笨拙。
比如,这个方法会依赖于你要替换的字符串长度(如果你用‘.ST’来替换‘STREET’,就需要在s[-6:
]中查找‘STREET’,然后再取s[:
-6]。
你难道还想半年后回来继续修改BUG?
反正我是不想。
4.是时候转换到正则表达式了。
在python中,所有的正则表达式相关功能都包含在re模块中。
5.注意第一个参数‘ROAD$’,这是一个匹配‘ROAD’仅仅出现在字符串结尾的正则表达式。
$表示“字符串结尾”。
(还有一个相应的表示“字符串开头”的字符^)。
正则表达式模块的re.sub()函数可以做字符串替换,它在字符串s中用正则表达式‘ROAD$’来搜索并替换成‘RD.’。
它只会匹配字符串结尾的‘ROAD’,而不会匹配到‘BROAD’中的‘ROAD’,因为这种情况它在字符串的中间。
^匹配字符串开始.$匹配字符串结尾
继续我的处理街道地址的故事。
我很快发现,在之前的例子中,匹配地址结尾的‘ROAD’不够好。
因为并不是所有的地址结尾都有它。
一些地址简单的用一个街道名结尾。
大部分的情况下不会有问题,但如果街道的名字就叫‘BROAD’,这个时候,正则表达式会匹配到‘BROAD’的最后4个字符,这并不是我想要的。
>>>s='100BROAD'
>>>re.sub('ROAD$','RD.',s)
'100BRD.'
>>>re.sub('\\bROAD$','RD.',s)①
'100BROAD'
>>>re.sub(r'\bROAD$','RD.',s)②
'100BROAD'
>>>s='100BROADROADAPT.3'
>>>re.sub(r'\bROAD$','RD.',s)③
'100BROADROADAPT.3'
>>>re.sub(r'\bROAD\b','RD.',s)④
'100BROADRD.APT3'
1.我真正想要的‘ROAD’,必须是匹配到字符串结尾,并且是独立的词(他不能是某个比较长的词的一部分)。
为了在正则表达式中表达这个独立的词,你可以使用‘\b’。
它的意思是“在右边必须有一个分隔符”。
在python中,比较复杂的是‘\’字符必须被转义,这有的时候会导致‘\’字符传染(想想可能还要对\字符做转义的情况)。
这也是为什么perl中的正则表达式比python的简单的原因之一。
另一方面,perl会在正则表达式中混合其他非正则表达式的语法,如果出现了bug,那么很难区分这个bug是在正则表达式中,还是在其他的语法部分。
2.为了解决‘\’字符传染的问题,可以使用原始字符串。
这只需要在字符串的前面添加一个字符‘r’。
它告诉python,字符串中没有任何字符需要转义。
‘\t’是一个制表符,但r‘\t’只是一个字符‘\’紧跟着一个字符t。
我建议在处理正则表达式的时候总是使用原始字符串。
否则,会因为理解正则表达式而消耗大量时间(本身正则表达式就已经够让人困惑的了)。
3.哎,不幸的是,我发现了更多的地方与我的逻辑背道而驰。
街道地址包含了独立的单词‘ROAD’,但并不是在字符串尾,因为街道后面还有个单元号。
因为'ROAD'并不是最靠后,就不能匹配,因此re.sub()最后没有做任何的替换,只是返回了一个原始的字符串,这并不是你想要的。
4.为了解决这个问题,我删除了正则表达式尾部的$,然后添加了一个\b。
现在这个正则表达式的意思是“在字符串的任意位置匹配独立的‘ROAD’单词”不管是在字符串的结束还是开始,或者中间的任意一个位置。
⁂
案例研究:
罗马数字
你肯定见过罗马数字,即使你不认识他们。
你可能在版权信息、老电影、电视、大学或者图书馆的题词墙看到(用CopyrightMCMXLVI”表示版权信息,而不是用“Copyright1946”),你也可能在大纲或者目录参考中看到他们。
这种系统的数字表达方式可以追溯到罗马帝国(因此而得名)。
在罗马数字中,有七个不同的数字可以以不同的方式结合起来表示其他数字。
∙I=1
∙V=5
∙X=10
∙L=50
∙C=100
∙D=500
∙M=1000
下面是几个通常的规则来构成罗马数字:
∙大部分时候用字符相叠加来表示数字。
I是1,II是2,III是3。
VI是6(挨个看来,是“5和1”的组合),VII是7,VIII是8。
∙含有10的字符(I,X,C和M)最多可以重复出现三个。
为了表示4,必须用同一位数的下一个更大的数字5来减去一。
不能用IIII来表示4,而应该是IV(意思是比5小1)。
40写做XL(比50小10),41写做XLI,42写做XLII,43写做XLIII,44写做XLIV(比50小10并且比5小1)。
∙有些时候表示方法恰恰相反。
为了表示一个中间的数字,需要从一个最终的值来减。
比如:
9需要从10来减:
8是VIII,但9确是IX(比10小1),并不是VIII(I字符不能重复4次)。
90是XC,900是CM。
∙表示5的字符不能在一个数字中重复出现。
10只能用X表示,不能用VV表示。
100只能用C表示,而不是LL。
∙罗马数字是从左到右来计算,因此字符的顺序非常重要。
DC表示600,而CD完全是另一个数字400(比500小100)。
CI是101,IC不是一个罗马数字(因为你不能从100减1,你只能写成XCIX,表示比100小10,且比10小1)。
检查千位数
怎么验证一个字符串是否是一个合法的罗马数字呢?
我们可以每次取一个字符来处理。
因为罗马数字总是从高位到低位来书写。
我们从最高位的千位开始。
表示1000或者更高的位数值,方法是用一系列的M来重复表示。
>>>importre
>>>pattern='^M?
M?
M?
$'①
>>>re.search(pattern,'M')②
<_sre.SRE_Matchobjectat0106FB58>
>>>re.search(pattern,'MM')③
<_sre.SRE_Matchobjectat0106C290>
>>>re.search(pattern,'MMM')④
<_sre.SRE_Matchobjectat0106AA38>
>>>re.search(pattern,'MMMM')⑤
>>>re.search(pattern,'')⑥
<_sre.SRE_Matchobjectat0106F4A8>
1.这个模式有三部分。
^表示必须从字符串开头匹配。
如果没有指定^,这个模式将在任意位置匹配M,这个可能并不是你想要的。
你需要确认是否要匹配字符串开始的M,还是匹配单个M字符。
因为它重复了三次,你要在一行中的任意位置匹配0到3次的M字符。
$匹配字符串结束。
当它和匹配字符串开始的^一起使用,表示匹配整个字符串。
没有任何一个字符可在M的前面或者后面。
2.re模块最基本的方法是search()函数。
它使用正则表达式来匹配字符串(M)。
如果成功匹配,search()返回一个匹配对象。
匹配对象中有很多的方法来描述这个匹配结果信息。
如果没有匹配到,search()返回None。
你只需要关注search()函数的返回值就可以知道是否匹配成功。
‘M’被正则表达式匹配到了。
原因是正则表达式中的第一个可选的M匹配成功,第二个和第三个被忽略掉了。
3.‘MM’匹配成功。
因为正则表达式中的第一个和第二个可选的M匹配到,第三个被忽略。
4.‘MMM’匹配成功。
因为正则表达式中的所有三个M都匹配到。
5.‘MMMM’匹配失败。
正则表达式中所有三个M都匹配到,接着正则表达式试图匹配字符串结束,这个时候失败了。
因此search()函数返回None。
6.有趣的是,空字符串也能匹配成功,因为正则表达式中的所有M都是可选的。
检查百位数
?
表示匹配是可选的
百位的匹配比千位复杂。
根据值的不同,会有不同的表达方式。
∙100=C
∙200=CC
∙300=CCC
∙400=CD
∙500=D
∙600=DC
∙700=DCC
∙800=DCCC
∙900=CM
因此会有四种可能的匹配模式:
∙CM
∙CD
∙可能有0到3个字符C(0个表示千位为0)。
∙D紧跟在0到3个字符C的后面。
这两个模式还可以组合起来表示:
∙一个可选的D,后面跟着0到3个字符C。
下面的例子展示了怎样在罗马数字中验证百位。
>>>importre
>>>pattern='^M?
M?
M?
(CM|CD|D?
C?
C?
C?
)$'①
>>>re.search(pattern,'MCM')②
<_sre.SRE_Matchobjectat01070390>
>>>re.search(pattern,'MD')③
<_sre.SRE_Matchobjectat01073A50>
>>>re.search(pattern,'MMMCCC')④
<_sre.SRE_Matchobjectat010748A8>
>>>re.search(pattern,'MCMC')⑤
>>>re.search(pattern,'')⑥
<_sre.SRE_Matchobjectat01071D98>
1.这个正则表达式的写法从上面千位的匹配方法接着往后写。
检查字符串开始(^),然后是千位,后面才是新的部分。
这里用圆括号定义了三个不同的匹配模式,他们是用竖线分隔的:
CM,CD和D?
C?
C?
C?
(这表示是一个可选的D,以及紧跟的0到3个可选的字符C)。
正则表达式按从左到右的顺序依次匹配,如果第一个CM匹配成功,用竖线分隔这几个中的后面其他的都会被忽略。
2.‘MCM’匹配成功。
因为第一个M匹配到,第二个和第三个M被忽略。
后面的CM匹配到(因此后面的CD和D?
C?
C?
C?
根本就不被考虑匹配了)。
MCM在罗马数字中表示1900。
3.‘MD’匹配成功。
因为第一个M匹配到,第二个和第三个M被忽略。
然后D?
C?
C?
C?
匹配到D(后面的三个C都是可选匹配的,都被忽略掉)。
MD在罗马数字中表示1500。
4.‘MMMCCC’匹配成功。
因为前面三个M都匹配到。
后面的D?
C?
C?
C?
匹配CCC(D是可选的,它被忽略了)。
MMMCCC在罗马数字中表示3300。
5.‘MCMC’匹配失败。
第一个M被匹配,第二个和第三个M被忽略,然后CM匹配成功。
紧接着$试图匹配字符串结束,但后面是C,匹配失败。
C也不能被D?
C?
C?
C?
匹配到,因为CM和它只能匹配其中一个,而CM已经匹配过了。
6.有趣的是,空字符串仍然可以匹配成功。
因为所有的M都是可选的,都可以被忽略。
并且后面的D?
C?
C?
C?
也是这种情况。
哈哈,看看正则表达式如此快速的处理了这些令人厌恶的东西。
你已经可以找到千位数和百位数了!
后面的十位和个位的处理和千位、百位的处理是一样的。
但我们可以看看怎么用另一种方式来写这个正则表达式。
⁂
使用语法{n,m}
{1,4}匹配1到4个前面的模式
在上一节中,你处理过同样的字符可以重复0到3次的情况。
实际上,还有另一种正则表达式的书写方式可以表达同样的意思,而且这种表达方式更具有可读性。
首先看看我们在前面例子中使用的方法。
>>>importre
>>>pattern='^M?
M?
M?
$'
>>>re.search(pattern,'M')①
<_sre.SRE_Matchobjectat0x008EE090>
>>>pattern='^M?
M?
M?
$'
>>>re.search(pattern,'MM')②
<_sre.SRE_Matchobjectat0x008EEB48>
>>>pattern='^M?
M?
M?
$'
>>>re.search(pattern,'MMM')③
<_sre.SRE_Matchobjectat0x008EE090>
>>>re.search(pattern,'MMMM')④
>>>
1.正则表达式匹配字符串开始,然后是第一个可选的字符M,但没有第二个和第三个M(没问题!
因为他们是可选的),接着是字符串结尾。
2.正则表达式匹配字符串开始,然后是第一个和第二个M,第三个被忽略(因为它是可选的),最后匹配字符串结尾。
3.正则表达式匹配字符串开始,然后是三个M,接着是字符串结尾。
4.正则表达式匹配字符串开始,然后是三个M,但匹配字符串结尾失败(因为后面还有个M)。
因此,这次匹配返回None。
>>>pattern='^M{0,3}$'①
>>>re.search(pattern,'M')②
<_sre.SRE_Matchobjectat0x008EEB48>
>>>re.search(pattern,'MM')③
<_sre.SRE_Matchobjectat0x008EE090>
>>>re.search(pattern,'MMM')④
<_sre.SRE_Matchobjectat0x008EEDA8>
>>>re.search(pattern,'MMMM')⑤
>>>
1.这个正则表达式的意思是“匹配字符串开始,然后是任意的0到3个M字符,再是字符串结尾”。
0和3的位置可以写任意的数字。
如果你想表示可以匹配的最小次数为1次,最多为3次M字符,可以写成M{1,3}。
2.匹配字符串开始,然后匹配了1次M,这在0到3的范围内,接着是字符串结尾。
3.匹配字符串开始,然后