上传文件至 /

This commit is contained in:
zwb 2024-12-26 10:00:35 +08:00
parent 2bacd3eaea
commit 79acfab8f0
4 changed files with 3705 additions and 0 deletions

232
11-Python日期时间.md Normal file
View File

@ -0,0 +1,232 @@
# 11-Python日期时间
time 和 calendar 模块可以用于格式化日期和时间。
时间间隔是以秒为单位的浮点小数。
每个时间戳都以自从1970年1月1日午夜历元经过了多长时间来表示。
例:
\#!/usr/bin/python
import time; # 引入time模块
ticks = time.time()
print "当前时间戳为:", ticks
输出结果:
当前时间戳为: 1459994552.51
时间戳单位最适于做日期运算。但是1970年之前的日期就无法以此表示了。太遥远的日期也不行UNIX和Windows只支持到2038年。
**什么是时间元组?**
很多Python函数用一个元组装起来的9组数字处理时间:
| 序号 | 字段 | 值 |
| ---- | ------------ | ------------------------------------ |
| 0 | 4位数年 | 2008 |
| 1 | 月 | 1 到 12 |
| 2 | 日 | 1到31 |
| 3 | 小时 | 0到23 |
| 4 | 分钟 | 0到59 |
| 5 | 秒 | 0到61 (60或61 是闰秒) |
| 6 | 一周的第几日 | 0到6 (0是周一) |
| 7 | 一年的第几日 | 1到366 (儒略历) |
| 8 | 夏令时 | -1, 0, 1, -1是决定是否为夏令时的旗帜 |
上述也就是struct_time元组。这种结构具有如下属性
| 序号 | 属性 | 值 |
| ---- | -------- | ------------------------------------ |
| 0 | tm_year | 2008 |
| 1 | tm_mon | 1 到 12 |
| 2 | tm_mday | 1 到 31 |
| 3 | tm_hour | 0 到 23 |
| 4 | tm_min | 0 到 59 |
| 5 | tm_sec | 0 到 61 (60或61 是闰秒) |
| 6 | tm_wday | 0到6 (0是周一) |
| 7 | tm_yday | 1 到 366(儒略历) |
| 8 | tm_isdst | -1, 0, 1, -1是决定是否为夏令时的旗帜 |
**获取当前时间**
从返回浮点数的时间辍方式向时间元组转换只要将浮点数传递给如localtime之类的函数。
\#!/usr/bin/python
import time
localtime = time.localtime(time.time())
print "本地时间为 :", localtime
输出结果:
本地时间为 : time.struct_time(tm_year=2016, tm_mon=4, tm_mday=7, tm_hour=10, tm_min=3, tm_sec=27,
tm_wday=3, tm_yday=98, tm_isdst=0)
获取格式化的时间
可以根据需求选取各种格式但是最简单的获取可读的时间模式的函数是asctime():
\#!/usr/bin/python
import time
localtime = time.asctime( time.localtime(time.time()) )
print "本地时间为 :", localtime
输出结果:
本地时间为 : Thu Apr 7 10:05:21 2016
\----------------------------
可以直接使用ctime()
In [1]: from time import ctime
In [5]: ctime()
Out[5]: 'Fri Apr 14 10:44:46 2017'
**格式化日期**
我们可以使用 time 模块的 strftime 方法来格式化日期:
time.strftime(format[, t])
\#!/usr/bin/python
import time
\# 格式化成2016-03-20 11:45:39形式
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
date +
\# 格式化成Sat Mar 28 22:24:24 2016形式
print time.strftime("%a %b %d %H:%M:%S %Y", time.localtime())
\# 将格式字符串转换为时间戳
a = "Sat Mar 28 22:24:24 2016"
print time.mktime(time.strptime(a,"%a %b %d %H:%M:%S %Y"))
输出结果:
2016-04-07 10:25:09
Thu Apr 07 10:25:09 2016
1459175064.0
python中时间日期格式化符号
• %y 两位数的年份表示00-99
• %Y 四位数的年份表示000-9999
• %m 月份01-12
• %d 月内中的一天0-31
• %H 24小时制小时数0-23
• %I 12小时制小时数01-12
• %M 分钟数00=59
• %S 秒00-59
• %a 本地简化星期名称
• %A 本地完整星期名称
• %b 本地简化的月份名称
• %B 本地完整的月份名称
• %c 本地相应的日期表示和时间表示
• %j 年内的一天001-366
• %p 本地A.M.或P.M.的等价符
• %U 一年中的星期数00-53星期天为星期的开始
• %w 星期0-6星期天为星期的开始
• %W 一年中的星期数00-53星期一为星期的开始
• %x 本地相应的日期表示
• %X 本地相应的时间表示
• %Z 当前时区的名称
• %% %号本身
**获取某月日历**
Calendar模块有很广泛的方法用来处理年历和月历例如打印某月的月历
```python
#!/usr/bin/python3.8
import calendar
cal = calendar.month(2016, 1)
print "以下输出2016年1月份的日历:"
print cal
```
输出结果:
以下输出2016年1月份的日历:
January 2016
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
**Time 模块**
Time 模块包含了以下内置函数,既有时间处理相的,也有转换时间格式的:
| 序号 | 函数及描述 |
| ---- | ------------------------------------------------------------ |
| 1 | time.altzone 返回格林威治西部的夏令时地区的偏移秒数。如果该地区在格林威治东部会返回负值(如西欧,包括英国)。对夏令时启用地区才能使用。 |
| 2 | time.asctime([tupletime]) 接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"2008年12月11日 周二18时07分14秒的24个字符的字符串。 |
| 3 | time.clock( ) 用以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时比time.time()更有用。 |
| 4 | time.ctime([secs]) 作用相当于asctime(localtime(secs))未给参数相当于asctime() |
| 5 | time.gmtime([secs]) 接收时间辍1970纪元后经过的浮点秒数并返回格林威治天文时间下的时间元组t。注t.tm_isdst始终为0 |
| 6 | time.localtime([secs]) 接收时间辍1970纪元后经过的浮点秒数并返回当地时间下的时间元组tt.tm_isdst可取0或1取决于当地当时是不是夏令时。 |
| 7 | time.mktime(tupletime) 接受时间元组并返回时间辍1970纪元后经过的浮点秒数。 |
| 8 | time.sleep(secs) 推迟调用线程的运行secs指秒数。 |
| 9 | time.strftime(fmt[,tupletime]) 接收以时间元组并返回以可读字符串表示的当地时间格式由fmt决定。 |
| 10 | time.strptime(str,fmt='%a %b %d %H:%M:%S %Y') 根据fmt的格式把一个时间字符串解析为时间元组。 |
| 11 | time.time( ) 返回当前时间的时间戳1970纪元后经过的浮点秒数。 |
| 12 | time.tzset() 根据环境变量TZ重新初始化时间相关设置。 |
Time模块包含了以下2个非常重要的属性
| 序号 | 属性及描述 |
| ---- | ------------------------------------------------------------ |
| 1 | time.timezone 属性time.timezone是当地时区未启动夏令时距离格林威治的偏移秒数>0美洲;<=0大部分欧洲亚洲非洲。 |
| 2 | time.tzname 属性time.tzname包含一对根据情况的不同而不同的字符串分别是带夏令时的本地时区名称和不带的。 |
**日历Calendar模块**
此模块的函数都是日历相关的,例如打印某月的字符月历。
星期一是默认的每周第一天星期天是默认的最后一天。更改设置需调用calendar.setfirstweekday()函数。
模块包含了以下内置函数:
| 序号 | 函数及描述 |
| ---- | ------------------------------------------------------------ |
| 1 | calendar.calendar(year,w=2,l=1,c=6) 返回一个多行字符串格式的year年年历3个月一行间隔距离为c。 每日宽度间隔为w字符。每行长度为21* W+18+2* C。l是每星期行数。 |
| 2 | calendar.firstweekday( ) 返回当前每周起始日期的设置。默认情况下首次载入caendar模块时返回0即星期一。 |
| 3 | calendar.isleap(year) 是闰年返回True否则为false。 |
| 4 | calendar.leapdays(y1,y2) 返回在Y1Y2两年之间的闰年总数。 |
| 5 | calendar.month(year,month,w=2,l=1) 返回一个多行字符串格式的year年month月日历两行标题一周一行。每日宽度间隔为w字符。每行的长度为7* w+6。l是每星期的行数。 |
| 6 | calendar.monthcalendar(year,month) 返回一个整数的单层嵌套列表。每个子列表装载代表一个星期的整数。Year年month月外的日期都设为0;范围内的日子都由该月第几日表示从1开始。 |
| 7 | calendar.monthrange(year,month) 返回两个整数。第一个是该月的星期几的日期码第二个是该月的日期码。日从0星期一到6星期日;月从1到12。 |
| 8 | calendar.prcal(year,w=2,l=1,c=6) 相当于 print calendar.calendar(year,w,l,c). |
| 9 | calendar.prmonth(year,month,w=2,l=1) 相当于 print calendar.calendaryearwlc。 |
| 10 | calendar.setfirstweekday(weekday) 设置每周的起始日期码。0星期一到6星期日。 |
| 11 | calendar.timegm(tupletime) 和time.gmtime相反接受一个时间元组形式返回该时刻的时间辍1970纪元后经过的浮点秒数。 |
| 12 | calendar.weekday(year,month,day) 返回给定日期的日期码。0星期一到6星期日。月份为 1一月 到 1212月。 |
**其他相关模块和函数**
在Python中其他处理日期和时间的模块还有
• [datetime](http://docs.python.org/library/datetime.html#module-datetime)模块
• [pytz](http://www.twinsun.com/tz/tz-link.htm)模块
• [dateutil](http://labix.org/python-dateutil)模块
Python实现查找指定文件夹内指定时间之后修改过的文件
```python
#!/usr/bin/python
import os
import time
import datetime
time1 = '2010-4-1 12:33' #指定时间
root1 = 'D:\\WWW' #指定文件夹
def txt_2_time(txt):
l = txt.replace(' ','-').replace(':','-').split('-')
l = map(int,l)
s = datetime.datetime(*l)
t = time.mktime(s.timetuple())
return t
for root,dirs,files in os.walk(root1):
for file in files:
f = os.path.join(root,file)
mtime = os.path.getmtime(f)
if os.path.splitext(f)[1] in ('.html','.php') and mtime > txt_2_time(time1):
print f,time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(mtime))
```

480
12-Python正则表达.md Normal file
View File

@ -0,0 +1,480 @@
# 12-Python正则表达
**正则表达式**
wing忠告搞运维的童鞋如果没有接触过shell里面的正则表达式建议先跳过本章
**导语**
正则表达式是一个特殊的字符序列,用来检查一个字符串是否与某种模式匹配。
Python提供的是 Perl 风格的正则表达式模式。
re 模块使 Python拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
re 模块也提供与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。
**正则表达式的使用有两种方式:**
1.使用正则之前先编译正则
2.使用正则之前不编译正则
**1.使用正则之前先编译正则**
正则表达式被编译成 'RegexObject' 实例,可以为不同的操作提供方法,如模式匹配搜索或
字符串替换。
\>>> import re
\>>> p=re.compile('n+') #先编译正则表达式,生成一个表达式对象
\>>> p.match('nnnning').group() #再使用表达式去进行匹配
'nnnn'
group()返回被 RE 匹配的字符串 //'nnnn'
start()返回匹配开始的位置 //0
end()返回匹配结束位置的下一个位置 //4
span()返回一个元组包含匹配 (开始,结束) 的位置 //(0,4) 不包含下边界
**2.使用正则之前不编译正则**
利用match和search帮我们编译
**re.match():**
re.match 尝试从字符串的起始位置匹配一个模式如果不是在起始位置匹配成功match()
就返回none。
语法:
re.match(pattern, string, flags=0)
参数说明:
| 参数 | 描述 |
| ------- | ------------------------------------------------------------ |
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
匹配成功re.match方法返回一个匹配的对象否则返回None。
使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
| 匹配对象方法 | 描述 |
| ------------ | ------------------------------------------------------------ |
| group(num=0) | 匹配的整个表达式的字符串group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
| groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
例 1
\#!/usr/bin/python3
import re
print(re.match('www', 'www.fklinux.com').span()) # 在起始位置匹配
print(re.match('com', 'www.fklinux.com'))
输出结果:
(0, 3)
None
例 2
\#!/usr/bin/python
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
\#上面的re.M|re.I是正则表达式修饰符 - 可选标志,后面马上会讲到
if matchObj:
print "matchObj.group() : ", matchObj.group()
print "matchObj.group(1) : ", matchObj.group(1)
print "matchObj.group(2) : ", matchObj.group(2)
else:
print "No match!!"
执行结果:
matchObj.group() : Cats are smarter than dogs
matchObj.group(1) : Cats
matchObj.group(2) : smarter
**re.search():**
re.search 扫描整个字符串并返回第一个成功的匹配。
函数语法:
re.search(pattern, string, flags=0)
函数参数说明:
| 参数 | 描述 |
| ------- | ------------------------------------------------------------ |
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串。 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
匹配成功re.search方法返回一个匹配的对象否则返回None。
使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
| 匹配对象方法 | 描述 |
| ------------ | ------------------------------------------------------------ |
| group(num=0) | 匹配的整个表达式的字符串group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
| groups() | 返回一个包含所有小组字符串的元组 |
例 1
\>>> print(re.search('www', 'www.fklinux.com'))
<_sre.SRE_Match object at 0x7fbbe5816c60> #python2只显示内存地址
<_sre.SRE_Match object; span=(0, 3), match='www'> #python3的显示结果
\>>> print(re.search('com', 'www.fklinux.com'))
<_sre.SRE_Match object at 0x7fbbe5816c60> #python2只显示内存地址
<_sre.SRE_Match object; span=(12, 15), match='com'> #python3的显示结果
\>>> print(re.search('www', 'www.fklinux.com').span()) #加上span()方法处理一下
(0, 3)
\>>> print(re.search('com', 'www.fklinux.com').span())
(12, 15)
例 2
\#!/usr/bin/python
import re
line = "Cats are smarter than dogs";
searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I) #每个小括号一个分组
if searchObj:
print "searchObj.group() : ", searchObj.group()
print "searchObj.group(1) : ", searchObj.group(1)
print "searchObj.group(2) : ", searchObj.group(2)
else:
print "Nothing found!!"
执行结果:
searchObj.group() : Cats are smarter than dogs #
searchObj.group(1) : Cats
searchObj.group(2) : smarter
例3
\>>> print line
wig wing winng winnng winnnng
\>>> re.search('(wig)( +)(wing)',line).groups() #所有匹配的组的内容
('wig', ' ', 'wing')
\>>> re.search('(wig)( +)(wing)',line).group() #所有匹配的组的内容
'wig wing'
\>>> re.search('(wig)( +)(wing)',line).group(0) #所有匹配的组的内容
'wig wing'
\>>> re.search('(wig)( +)(wing)',line).group(1) #第一组匹配的内容
'wig'
\>>> re.search('(wig)( +)(wing)',line).group(2) #第二组匹配的内容
' '
\>>> re.search('(wig)( +)(wing)',line).group(3) #第三组匹配的内容
'wing'
**re.match与re.search的区别**
re.match
只匹配字符串的开始如果字符串开始不符合正则表达式则匹配失败函数返回None。
re.search
匹配整个字符串,直到找到一个匹配。
例:
\#!/usr/bin/python
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
print "match --> matchObj.group() : ", matchObj.group()
else:
print "No match!!"
matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
print "search --> matchObj.group() : ", matchObj.group()
else:
print "No match!!"
运行结果:
No match!!
search --> matchObj.group() : dogs
**正则表达式修饰符 - 可选标志**
正则表达式可以包含一些可选标志修饰符来控制匹配的模式
修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。
如: re.I | re.M 被设置成 I 和 M 标志
| 修饰符 | 描述 |
| ------ | ------------------------------------------------------------ |
| re.I | 使匹配对大小写不敏感 |
| re.L | 做本地化识别locale-aware匹配 |
| re.M | 多行匹配,影响 ^ 和 $ |
| re.S | 使 . 匹配包括换行在内的所有字符 |
| re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
| re.X | 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。 |
[root@wing python]# cat a.py
\#!/usr/bin/env python
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g.') #这里的点不包括换行所以匹配到的是ga
print r.search(line).group()
[root@wing python]# python a.py
ga
\------------------------------------------------
[root@wing python]# cat a.py
\#!/usr/bin/env python
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g.',re.S) #这里的点包括换行所以匹配到的是g和换行
print r.search(line).group()
[root@wing python]# python a.py
g
[root@wing python]#
\-----------------------------------------------
[root@wing python]# cat a.py
\#!/usr/bin/env python
\#coding=utf-8
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g. #hello,这是注释',re.X) #用了re.X后可以给正则表达式添加注释
print r.search(line).group()
[root@wing python]# python a.py
ga
======================================================
**findall()**
findall()和 search()相似之处:
在于二者都执行字符串搜索
findall()和 match()与search()不同之处:
findall()总返回一个列表(match和search返回的是元组)
如果 findall()没有找到匹配的部分,会返回空列表
如果成功找到匹配部分,则返回所有匹配部分的列表(按从左到右出现的顺序排列)
语法:
findall(rule , target [,flag] )
rule 规则
target 目标字符串
flag 规则选项选项功能和compile和search的flag一样。返回结果是一个列表 中间存放的是符合规则的字符串。如果没有符合规则的字符串被找到,就返回一个空列表。
例子:
\>>> re.findall('car', 'car')
['car']
\>>> re.findall('car', 'scary')
['car']
\>>> re.findall('car', 'carry the barcardi to the car')
['car', 'car', 'car']
======================================================
**检索和替换**
Python 的re模块提供了re.sub和re.subn用于替换字符串中的匹配项。
### re.sub()
:
语法:
re.sub(pattern, repl, string, max=0)
返回的字符串是在字符串中用 RE 最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回。
可选参数 count 是模式匹配后替换的最大次数count 必须是非负整数。缺省值是 0 表示替换所有的匹配。
例:
\#!/usr/bin/python
import re
phone = "2004-959-559 # This is Phone Number"
\# Delete Python-style comments
num = re.sub(r'#.*$', "", phone)
print "Phone Num : ", num
\# Remove anything other than digits
num = re.sub(r'\D', "", phone)
print "Phone Num : ", num
执行结果:
Phone Num : 2004-959-559
Phone Num : 2004959559
\--------------------------------------------
**re.subn():**
subn()和 sub()一样,但它还返回一个表示替换次数的数字,替换后的字符串和表示替换次数的数字作为一个元组的元素返回。
\>>> re.sub('[ae]', 'X', 'abcdef')
'XbcdXf'
\>>> re.subn('[ae]', 'X', 'abcdef')
('XbcdXf', 2)
==================================================
## re.split():
re 模块和正则表达式对象的方法 split()与字符串的 split()方法相似,前者是根据正则表达式模式分隔字符串,后者是根据固定的字符串分割,因此与后者相比,显著提升了字符分割的能力。如果你不想在每个模式匹配的地方都分割字符串,你可以通过设定一个值参数(非零)来指定分割的最大次数。
如果分隔符没有使用由特殊符号表示的正则表达式来匹配多个模式,那re.split()和string.split()的执行过程是一样的:
比如:在每一个冒号处分隔
\>>> re.split(':', 'str1:str2:str3')
['str1', 'str2', 'str3']
使用正则表达式做分割符:
\>>> line='who ----> are ----> you'
\>>> re.split(r'\s*-+>\s*',line)
['who', 'are', 'you']
==================================================
**正则表达式模式**
模式字符串使用特殊的语法来表示一个正则表达式:
• 字母和数字表示他们自身
• 多数字母和数字前加一个反斜杠时会拥有不同的含义
• 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义
• 反斜杠本身需要使用反斜杠转义
• 由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于'\\t')匹配相应的特殊字符。
下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。
| 模式 | 描述 |
| ----------- | ------------------------------------------------------------ |
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的末尾。 |
| . | 匹配任意字符除了换行符当re.DOTALL标记被指定时则可以匹配包括换行符的任意字符。 |
| [...] | 用来表示一组字符,单独列出:[amk] 匹配 'a''m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
| * | 匹配0个或多个的表达式。 |
| + | 匹配1个或多个的表达式。 |
| ? | 匹配0个或1个由前面的正则表达式定义的片段非贪婪方式 |
| {n} | 精确匹配n个前面表达式。 |
| {n,} | 匹配最少n个前面表达式。 |
| {n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
| a\|b | 匹配a或b |
| (re) | 既匹配括号内的表达式,也表示一个组 |
| (?: re) | 类似 (...), 但是不表示一个组 |
| (?#...) | 注释. |
| \w | 匹配字母数字及下划线 |
| \W | 匹配非字母数字及下划线 |
| \s | 匹配任意空白字符,等价于 [\t\n\r\f]. |
| \S | 匹配任意非空字符 |
| \d | 匹配任意数字,等价于 [0-9]. |
| \D | 匹配任意非数字 |
| \A | 匹配字符串开始,类似于^,但他主要是为了那些没有caret(^)字符的键盘准备的 |
| \Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。类似于$,但他主要是为了那些没有($)字符的键盘准备的 |
| \b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, 等. | 匹配一个换行符。匹配一个制表符。等 |
| \1...\9 | 匹配第n个分组的子表达式。 |
| \10 | 匹配第n个分组的子表达式如果它经匹配。否则指的是八进制字符码的表达式。 |
注意:
1.上面有些正则如果不管用,就说明必须用原始字符串,比如'er\b'必须写成r'er\b'才会生效
原因是 ASCII 字符和正则表达式特殊字符间所产生的冲突。比如,特殊符号“"b”"ASCII 字符中代表退格键,但同时"\b"也是一个正则表达式的特殊符号, 代表"匹配一个单词边界"。为了让 RE 编译器把两个字符"\b"当成你想要表达的字符串,而不是一个退格键,你需要用另一个反斜线对它进行转义,即可以这样写:"\\b"或者r"\b"。
2.*? +? ?? 最小匹配:
* + ? 通常都是尽可能多的匹配字符。有时候我们希望它尽可能少的匹配。比如一个 c 语言的注释 /* part 1 */ /* part 2 */ ,如果使用最大规则:
问号'?'有两种含义:
1)单独使用时表示匹配出现零次或一次的情况
2)紧跟在表示重复的元字符后面时,表示要求搜索引擎匹配的字符串越短越好。例如:(+?),原本+表示1次或多次现在只表示1次
例:
\>>> re.sub(r'n+','A','wing winnng is a good student')
'wiAg wiAg is a good studeAt'
\>>> re.sub(r'n+?','A','wing winnng is a good student')
'wiAg wiAAAg is a good studeAt'
3.(?:) 无捕获组
当你要将一部分规则作为一个整体对它进行某些操作,比如指定其重复次数时,你需要将这部分规则用 (?: ) 把它包围起来,而不能仅仅只用一对括号,那样将得到绝对出人意料的结果。
例:匹配字符串中重复的 ab
\>>> s=ababab abbabb aabaab
\>>> re.findall( r/b(?:ab)+/b , s )
['ababab']
如果仅使用一对括号,看看会是什么结果:
\>>> re.findall( r/b(ab)+/b , s )
['ab']
4.(?# ) 注释
Python 允许你在正则表达式中写入注释,在 (?#'') 之间的内容将被忽略。
\>>> s=ababab abbabb aabaab
\>>> re.findall('abab(?#注释)ab',s)
['ababab']
5.(?iLmsux) 编译选项指定
Python 的正则式可以指定一些选项,这个选项可以写在 findall 或 compile 的参数中,也可以写在正则式里,成为正则式的一部分。这在某些情况下会便利一些。具体的选项含义请看后面的 compile 函数的说明。
此处编译选项 i 等价于 IGNORECASE L 等价于 LOCAL m 等价于 MULTILINE s 等价于 DOTALL u 等价于 UNICODE x 等价于 VERBOSE 。
请注意它们的大小写。在使用时可以只指定一部分,比如只指定忽略大小写,可写为 (?i) ,要同时忽略大小写并使用多行模式,可以写为 (?im)
另外要注意选项的有效范围是整条规则,即写在规则的任何地方,选项都会对全部整条正则式有效。
\>>> s=ababab abbabb aabaab
\>>> re.findall('abAb(?#注释)**ab(?i)**',s)
['ababab']
6.前向界定与后向界定
有时候需要匹配一个跟在特定内容后面的或者在特定内容前面的字符串, Python 提供一个简便的前向界定和后向界定功能,或者叫前导指定和跟从指定功能。它们是:
(?<=…) 前向界定,括号中 ’…’ 代表你希望匹配的字符串的前面应该出现的字符串。
(?=…) 后向界定,括号中的 ’…’ 代表你希望匹配的字符串后面应该出现的字符串。
例: 你希望找出 c 语言的注释中的内容,它们是包含在 /**/ 之间,不过你并不希望匹配的结果把 /**/ 也包括进来,那么你可以这样用:
\>>> s=r/* comment 1 */ code /* comment 2 */
\>>> re.findall( r(?<=/\*).+?(?=\*/) , s )
[' comment 1 ', ' comment 2 ']
注意:这里我们仍然使用了最小匹配,以避免把整个字符串给匹配进去了。
要注意的是,前向界定括号中的表达式必须是常值,即你不可以在前向界定的括号里写正则式。比如你如果在下面的字符串中想找到被字母夹在中间的数字,你不可以用前向界定:
例:
\>>> s = aaa111aaa , bbb222 , 333ccc
\>>> re.findall( r(?<=[a-z]+)\d+(?=[a-z]+)' , s ) # 错误的用法
它会给出一个错误信息:
error: look-behind requires fixed-width pattern
不过如果你只要找出后面接着有字母的数字,你可以在后向界定写正则式:
\>>> re.findall( r\d+(?=[a-z]+), s )
['111', '333']
如果你一定要匹配包夹在字母中间的数字,你可以使用组( group )的方式
\>>> re.findall (r'[a-z]+(\d+)[a-z]+' , s )
['111']
除了前向界定前向界定和后向界定外,还有前向非界定和后向非界定,它的写法为:
(?<!...) 前向非界定,只有当你希望的字符串前面不是’…’ 的内容时才匹配
(?!...) 后向非界定,只有当你希望的字符串后面不跟着 ’…’ 内容时才匹配
接上例,希望匹配后面不跟着字母的数字
\>>> re.findall( r\d+(?!\w+) , s )
['222']
注意这里我们使用了 \w 而不是像上面那样用 [a-z] ,因为如果这样写的话,结果会是:
\>>> re.findall( r\d+(?![a-z]+) , s )
['11', '222', '33']
这和我们期望的似乎有点不一样。它的原因,是因为 111222 中的前两个数字也是满足这个要求的。
=============================================
**正则表达式实例**
字符匹配:
| 实例 | 描述 |
| ------ | -------------- |
| python | 匹配 "python". |
字符类:
| 实例 | 描述 |
| ----------- | --------------------------------- |
| [Pp]ython | 匹配 "Python" 或 "python" |
| rub[ye] | 匹配 "ruby" 或 "rube" |
| [aeiou] | 匹配中括号内的任意一个字母 |
| [0-9] | 匹配任何数字。类似于 [0123456789] |
| [a-z] | 匹配任何小写字母 |
| [A-Z] | 匹配任何大写字母 |
| [a-zA-Z0-9] | 匹配任何字母及数字 |
| [^aeiou] | 除了aeiou字母以外的所有字符 |
| [^0-9] | 匹配除了数字外的字符 |
特殊字符类:
| 实例 | 描述 |
| ---- | ------------------------------------------------------------ |
| . | 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 |
| \d | 匹配一个数字字符。等价于 [0-9]。 |
| \D | 匹配一个非数字字符。等价于 [^0-9]。 |
| \s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
| \S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
| \w | 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 |
| \W | 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |

445
13-Python异常处理.md Normal file
View File

@ -0,0 +1,445 @@
# 13-Python异常处理
**异常处理**
**导语**
在写代码的时候,经常会遇到异常。遇见异常并不是一件让人愉悦的事情。今天来一起详细了解异常。
python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误,你可以使用该功能来调试python程序。
\1. 异常处理
\2. 断言(Assertions)
**常用异常:**
Exception 它可以捕获任意(绝大部分)异常。
AttributeError 试图访问一个对象没有的树形比如foo.x但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类),代码没有正确对齐
IndexError 下标索引超出序列边界比如当x只有三个元素却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
**python标准异常**
[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\1.png](assets/1.png)]()
[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\2.png](assets/2.png)]()
[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\3.png](assets/3.png)]()
**什么是异常?**
异常即是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。
一般在Python无法正常处理程序时就会发生一个异常,异常是Python对象表示一
个错误。
**当Python脚本发生异常时需要捕获处理它否则程序会终止执行。**
\#!/usr/bin/env python
\# -*- coding: UTF-8 -*-
try:
print "%d" % (5 / 0)
except ZeroDivisionError:
print "除数不能为零"
else:
print "没有报错"
print "这是异常之后的代码" #如果没有上面的异常处理,下面的代码是不会执行的
for i in range(10):
print i
**捕捉异常**:
try/except语句用来检测try语句块中的错误从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序只需在try里捕获它。
语法:
try:
<语句> #运行别的代码
except <名字>
<语句> #如果在try部份引发了'name'异常
except <名字><数据>:
<语句> #如果引发了'name'异常,获得附加的数据
else:
<语句> #如果没有异常发生
**try的工作原理**
当开始一个try语句后python就在当前程序的上下文中作标记这样当异常出现时就可
以回到这里try子句先执行接下来会发生什么依赖于执行时是否出现异常。
\1. 如果当try后的语句执行时发生异常python就跳回到try并执行第一个匹配该异常的
except子句异常处理完毕控制流就通过整个try语句除非在处理异常时又引发新的异
常)。
\2. 如果在try后的语句里发生了异常却没有匹配的except子句异常将被递交到上层的
try或者到程序的最上层这样将结束程序并打印缺省的出错信息
\3. 如果在try子句执行时没有发生异常python将执行else语句后的语句如果有else的
然后控制流通过整个try语句。
[![file://C:\Users\86186\AppData\Local\Temp\.2IK5Q0\4.png](assets/4.png)]()
打开一个文件,在该文件中的写入内容,且并未发生异常:
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print "Error: 没有找到文件或读取文件失败"
else:
print "内容写入文件成功"
fh.close()
结果:
\# python test.py
内容写入文件成功
\# cat testfile # 查看写入的内容
这是一个测试文件,用于测试异常!!
打开一个文件,在该文件中的内容写入内容,但文件没有写入权限,发生了异常:
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print "Error: 没有找到文件或读取文件失败"
else:
print "内容写入文件成功"
fh.close()
在执行代码前为了测试方便,先去掉 testfile 文件的写权限
再执行以上代码:
$ python test.py #注意这里用的是普通用户
Error: 没有找到文件或读取文件失败
**使用except不带任何异常类型**
###
你可以不带任何异常类型使用except如下实例
try:
正常的操作
......................
except:
发生异常,执行这块代码
......................
else:
如果没有异常执行这块代码
以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式我们不能通过该程序
识别出具体的异常信息。因为它捕获所有的异常。
**使用except带多种异常类型**
也可以使用相同的except语句来处理多个异常信息
try:
正常的操作
................
except(Exception1[, Exception2[,...ExceptionN]]]):
发生以上多个异常中的一个,执行这块代码
......................
else:
如果没有异常执行这块代码
**try-finally 语句**
try-finally 语句无论是否发生异常都将执行最后的代码。
try:
<语句>
finally:
<语句> #退出try时总会执行
例1:
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
finally:
print "Error: 没有找到文件或读取文件失败"
例2
import time
try:
f=file("文件.py")
while True:
line = f.read()
if len(line)==0:
break
time.sleep(2)
print line,
finally:
f.close()
print "hello"
例3:
try:
fh = open("testfile", "w")
try:
fh.write("这是一个测试文件,用于测试异常!!")
finally:
print "关闭文件"
fh.close()
except IOError:
print "Error: 没有找到文件或读取文件失败"
**异常的参数**:
一个异常可以带上参数,可作为输出的异常信息参数。
你可以通过except语句来捕获异常的参数如下所示
try:
正常的操作
......................
except ExceptionType, Argument:
你可以在这输出 Argument 的值...
变量接收的异常值通常包含在异常的语句中。在元组的表单中变量可以接收一个或者多个
值。
元组通常包含错误字符串,错误数字,错误位置。
以下为单个异常的实例:
\#!/usr/bin/python
def temp_convert(var):
try:
return int(var)
except ValueError, Argument:
print "参数没 有包含数字\n", Argument
\# 调用函数
temp_convert("xyz")
以上程序执行结果如下:
$ python test.py
参数没有包含数字
invalid literal for int() with base 10: 'xyz'
**触发异常**
可以使用raise语句自己触发异常
raise语法格式
raise [Exception [, args [, traceback]]]
语句中Exception是异常的类型例如NameError参数是一个异常参数值。该参数是可
选的,如果不提供,异常的参数是"None"。
最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。
一个异常可以是一个字符串,类或对象。 Python的内核提供的异常大多数都是实例化的
类,这是一个类的实例的参数。
定义一个异常:
def functionName( level ):
if level < 1:
raise Exception("Invalid level!", level)
\# 触发异常后,后面的代码就不会再执行
注意:为了能够捕获异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。
例如我们捕获以上异常,"except"语句如下:
try:
正常逻辑
except "Invalid level!":
触发自定义异常
else:
其余代码
\#!/usr/bin/python
def mye( level ):
if level < 1:
raise Exception("Invalid level!", level)
\# 触发异常后,后面的代码就不会再执行
try:
mye(0) # 触发异常
except "Invalid level!":
print 1
else:
print 2
输出结果:
$ python test.py
Traceback (most recent call last):
File "test.py", line 11, in <module>
mye(0)
File "test.py", line 7, in mye
raise Exception("Invalid level!", level)
Exception: ('Invalid level!', 0)
**用户自定义异常:**
通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自
Exception类通过直接或间接的方式。
以下为与RuntimeError相关的实例,实例中创建了一个类基类为RuntimeError用于在
异常触发时输出更多的信息。
在try语句块中用户自定义的异常后执行except块语句变量 e 是用于创建Networkerror
类的实例。
class Networkerror(RuntimeError):
def __init__(self, arg):
self.args = arg
在你定义以上类后,你可以触发该异常,如下所示:
try:
raise Networkerror("Bad hostname")
except Networkerror,e:
print e.args
**万能异常**
在python的异常中有一个万能异常Exception它可以捕获任意异常。
例:
\#cat aa.py
s1 = 'hello'
try:
int(s1)
except Exception,e:
print e
执行结果:
\#python aa.py
invalid literal for int() with base 10: 'hello'
既然有这个万能异常其他异常是不是就可以忽略了当然不是对于特殊处理或提醒的异常需要先定义最后定义Exception来确保程序正常运行。
例:
s1 = 'hello'
try:
int(s1)
except KeyError,e:
print '键错误'
except IndexError,e:
print '索引错误'
except Exception, e:
print '错误'
**Python中何时使用断言 assert**
Python的assert是用来检查一个条件如果它为真就不做任何事。如果它为假则会抛出AssertError并且包含错误信息。例如
py> x = 23
py> assert x > 0, "x is not zero or negative"
py> assert x%2 == 0, "x is not an even number"
Traceback (most recent call last):
File "", line 1, in
AssertionError: x is not an even number
很多人用assert作为一个很快和容易的方法来在参数错误的时候抛出异常。但这样做是错的非常错误有两个原因。首先AssertError不是在测试参数时应该抛出的错误。你不应该像这样写代码
if
not isinstance(x, int):
raise AssertionError("not an int")
你应该抛出TypeError的错误assert会抛出错误的异常。
但是更危险的是有一个关于assert的困扰它可以被编译好然后从来不执行如果你用 O 或 oo 选项运行Python结果不保证assert表达式会运行到。当适当的使用assert时这是未来但是当assert不恰当的使用时它会让代码用-O执行时出错。
那什么时候应该使用assert没有特定的规则断言应该用于
防御型的编程
运行时检查程序逻辑
检查约定
程序常量
检查文档
(在测试代码的时候使用断言也是可接受的,是一种很方便的单元测试方法,你接受这些测试在用-O标志运行时不会做任何事。我有时在代码里使用assert False来标记没有写完的代码分支我希望这些代码运行失败。尽管抛出NotImplementedError可能会更好。
关于断言的意见有很多,因为它能确保代码的正确性。如果你确定代码是正确的,那么就没有用断言的必要了,因为他们从来不会运行失败,你可以直接移除这些断言。如果你确定检查会失败,那么如果你不用断言,代码就会通过编译并忽略你的检查。
在以上两种情况下会很有意思,当你比较肯定代码但是不是绝对肯定时。可能你会错过一些非常古怪的情况。在这个情况下,额外的运行时检查能帮你确保任何错误都会尽早地被捕捉到。
另一个好的使用断言的方式是检查程序的不变量。一个不变量是一些你需要依赖它为真的情况除非一个bug导致它为假。如果有bug最好能够尽早发现所以我们为它进行一个测试但是又不想减慢代码运行速度。所以就用断言因为它能在开发时打开在产品阶段关闭。
一个非变量的例子可能是,如果你的函数希望在它开始时有数据库的连接,并且承诺在它返回的时候仍然保持连接,这就是函数的不变量:
def
some_function(arg):
assert
not DB.closed()
...
\# code goes here
assert
not DB.closed()
return
result
断言本身就是很好的注释,胜过你直接写注释:
\# when we reach here, we know that n > 2
你可以通过添加断言来确保它:
assert n > 2
断言也是一种防御型编程。你不是让你的代码防御现在的错误,而是防止在代码修改后引发的错误。理想情况下,单元测试可以完成这样的工作,可是需要面对的现实是,它们通常是没有完成的。人们可能在提交代码前会忘了运行测试代码。有一个内部检查是另一个阻挡错误的防线,尤其是那些不明显的错误,却导致了代码出问题并且返回错误的结果。
加入你有一些if…elif 的语句块,你知道在这之前一些需要有一些值:
\# target is
expected to be one of x, y, or z, and nothing else.
if
target == x:
run_x_code()
elif target == y:
run_y_code()
else:
run_z_code()
假设代码现在是完全正确的。但它会一直是正确的吗?依赖的修改,代码的修改。如果依赖修改成 target = w 会发生什么会关系到run_w_code函数吗如果我们改变了代码但没有修改这里的代码可能会导致错误的调用 run_z_code 函数并引发错误。用防御型的方法来写代码会很好,它能让代码运行正确,或者立马执行错误,即使你在未来对它进行了修改。
在代码开头的注释很好的一步,但是人们经常懒得读或者更新注释。一旦发生这种情况,注释会变得没用。但有了断言,我可以同时对代码块的假设书写文档,并且在它们违反的时候触发一个干净的错误
assert target in
(x, y, z)
if
target == x:
run_x_code()
elif target == y:
run_y_code()
else:
assert target == z
run_z_code()
这样,断言是一种防御型编程,同时也是一种文档。我想到一个更好的方案:
if
target == x:
run_x_code()
elif target == y:
run_y_code()
elif target == z:
run_z_code()
else:
\# This can never happen. But just in
case it does...
raise RuntimeError("an unexpected error occurred")
按约定进行设计是断言的另一个好的用途。我们想象函数与调用者之间有个约定,比如下面的:
“"果你传给我一个非空字符串,我保证传会字符串的第一个字母并将其大写。”"
如果约定被函数或调用这破坏,代码就会出问题。我们说函数有一些前置条件和后置条件,所以函数就会这么写:
def first_upper(astring):
assert isinstance(astring, str) and len(astring) > 0
result = astring[0].upper()
assert isinstance(result, str) and len(result) == 1
assert result == result.upper()
return
result
按约定设计的目标是为了正确的编程,前置条件和后置条件是需要保持的。这是断言的典型应用场景,因为一旦我们发布了没有问题的代码到产品中,程序会是正确的,并且我们能安全的移除检查。
下面是我建议的不要用断言的场景:
不要用它测试用户提供的数据
不要用断言来检查你觉得在你的程序的常规使用时会出错的地方。断言是用来检查非常罕见的问题。你的用户不应该看到任何断言错误如果他们看到了这是一个bug修复它。
有的情况下,不用断言是因为它比精确的检查要短,它不应该是懒码农的偷懒方式。
不要用它来检查对公共库的输入参数,因为它不能控制调用者,所以不能保证调用者会不会打破双方的约定。
不要为你觉得可以恢复的错误用断言。换句话说,不用改在产品代码里捕捉到断言错误。
不要用太多断言以至于让代码很晦涩。

2548
14-Python面向对象.md Normal file

File diff suppressed because it is too large Load Diff