常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。html
但其实import加载的模块分为四个通用类别:java
1 使用python编写的代码(.py文件)node
2 已被编译为共享库或DLL的C或C++扩展python
3 包好一组模块的包git
4 使用C编写并连接到python解释器的内置模块程序员
若是你退出python解释器而后从新进入,那么你以前定义的函数或者变量都将丢失,所以咱们一般将程序写到文件中以便永久保存下来,须要时就经过python test.py方式去执行,此时test.py被称为脚本script。正则表达式
随着程序的发展,功能愈来愈多,为了方便管理,咱们一般将程序分红一个个的文件,这样作程序的结构更清晰,方便管理。这时咱们不只仅能够把这些文件当作脚本去执行,还能够把他们当作模块来导入到其余的模块中,实现了功能的重复利用,算法
模块的导入应该在程序开始的地方。shell
咱们能够在py文件中按照以下方式导入模块:数据库
import a from xx import b
固然还能够再导入模块的时候给模块起别名
from xx import heiheihei as yue
当作脚本运行:
__name__ 等于'__main__'
当作模块导入:
__name__= 模块名
咱们能够借助这个特性来控制咱们的py文件在不一样的应用场景下执行不一样的逻辑。
举个例子:
def say_hai(name): print('Hi, {}'.format(name)) # 下面的代码在当前文件以模块的方法被导入时是不会执行的 if __name__ == "__main__": print(__name__) input_name = input('your name:').strip() say_hai(input_name)
在内置数据类型(dict、list、set、tuple)的基础上, collections模块 还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可使用名字来访问元素内容的tuple
2.deque: 双端队列,能够快速的从另一侧追加和推出对象
3.Counter: 计数器,主要用来计数
4.OrderedDict: 有序字典
5.defaultdict: 带有默认值的字典
我们知道 tuple
能够表示不变集合,例如,一个点的二维坐标就能够表示成:
>>> p = (1, 2)
可是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。也就是说元祖在某些场合并不形象。
这时, namedtuple
就派上了用场:
>>> from collections import namedtuple >>> Point = namedtuple('Point', ['x', 'y']) >>> p = Point(1, 2) >>> p.x 1 >>> p.y 2
类似的,若是要用坐标和半径表示一个圆,也能够用 namedtuple
定义:
#namedtuple('名称', [属性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])
使用list存储数据时,按索引访问元素很快,可是插入和删除元素就很慢了,由于list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操做的双向列表,适合用于队列和栈:
>>> from collections import deque >>> q = deque(['a', 'b', 'c']) >>> q.append('x') >>> q.appendleft('y') >>> q deque(['y', 'a', 'b', 'c', 'x'])
deque 除了实现list的 append()
和 pop()
外,还支持 appendleft()
和 popleft()
,这样就能够很是高效地往头部添加或删除元素。
*Python3.6中,Dict已经能够记住key加入的顺序了。
若是咱们要显示保持Key的顺序,能够用 OrderedDict
:
>>> from collections import OrderedDict >>> d = dict([('a', 1), ('b', 2), ('c', 3)]) >>> d # dict的Key是无序的 {'a': 1, 'c': 3, 'b': 2} >>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)]) >>> od # OrderedDict的Key是有序的 OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意, OrderedDict
的Key会按照插入的顺序排列,不是Key自己排序:
>>> od = OrderedDict() >>> od['z'] = 1 >>> od['y'] = 2 >>> od['x'] = 3 >>> od.keys() # 按照插入的Key的顺序返回 ['z', 'y', 'x']
有以下值集合 [ 11 , 22 , 33 , 44 , 55 , 66 , 77 , 88 , 99 , 90. ..],将全部大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。
即: { 'k1' : 大于 66 , 'k2' : 小于 66 }
values = [11, 22, 33,44,55,66,77,88,99,90] my_dict = {} for value in values: if value>66: if my_dict.has_key('k1'): my_dict['k1'].append(value) else: my_dict['k1'] = [value] else: if my_dict.has_key('k2'): my_dict['k2'].append(value) else: my_dict['k2'] = [value]
from collections import defaultdict values = [11, 22, 33,44,55,66,77,88,99,90] my_dict = defaultdict(list) for value in values: if value>66: my_dict['k1'].append(value) else: my_dict['k2'].append(value)
使 用 dict
时,若是引用的Key不存在,就会抛出 KeyError
。若是但愿key不存在时,返回一个默认值,就能够用 defaultdict
:
>>> from collections import defaultdict >>> dd = defaultdict(lambda: 'N/A') >>> dd['key1'] = 'abc' >>> dd['key1'] # key1存在 'abc' >>> dd['key2'] # key2不存在,返回默认值 'N/A'
Counter类的目的是用来跟踪值出现的次数。
它是一个无序的容器类型,以字典的键值对形式存储,其中元素做为key,其计数做为value。
应用示例:
>>> from collections import Counter >>> c = Counter('abcdeabcdabcaba') >>> c Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
表示时间的三种方式
在Python中,一般有这三种方式来表示时间:时间戳、时间对象(struct_time)和格式化的时间字符串。
时间戳(timestamp) :一般来讲,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。
咱们运行“type(time.time())”,返回的是float类型。
通常能够把时间戳理解为计算机读懂的时间。
一个内置的struct_time对象。
struct_time元组共有9个元素共下图九个元素:
索引(Index) | 属性(Attribute) | 值(Values) |
---|---|---|
0 | tm_year(年) | 好比2011 |
1 | tm_mon(月) | 1 - 12 |
2 | tm_mday(日) | 1 - 31 |
3 | tm_hour(时) | 0 - 23 |
4 | tm_min(分) | 0 - 59 |
5 | tm_sec(秒) | 0 - 60 |
6 | tm_wday(weekday) | 0 - 6(0表示周一) |
7 | tm_yday(一年中的第几天) | 1 - 366 |
8 | tm_isdst(是不是夏令时) | 默认为0 |
时间对象是咱们可以理解和操做的时间。
格式化的时间字符串(Format String)首先,它是一个字符串。
这个字符串是按照固定的格式的,这个格式按以下格式:
%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 当前时区的名称 %% %号自己
咱们先导入time模块,快速认识一下python中表示时间的几种格式:
# 导入时间模块 import time # 时间戳 print(time.time()) # 1500875844.800804 # 时间字符串 print(time.strftime("%Y-%m-%d %X")) # '2017-07-24 13:54:37' print(time.strftime("%Y-%m-%d %H-%M-%S")) # '2017-07-24 13-55-04' # struct_time:localtime方法将一个时间戳转换为当前时区的struct_time格式 time.localtime() # time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=13, tm_min=59, tm_sec=37, tm_wday=0, tm_yday=205, tm_isdst=0)
小结:时间戳是计算机可以识别的时间;时间字符串是人可以看懂的时间;struct_time则是用来操做时间的。
几种时间格式之间的转换
#时间戳-->结构化时间 #time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致 #time.localtime(时间戳) #当地时间。例如咱们如今在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间 >>>time.gmtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) >>>time.localtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) #结构化时间-->时间戳 #time.mktime(结构化时间) >>>time_tuple = time.localtime(1500000000) >>>time.mktime(time_tuple) 1500000000.0
#结构化时间-->字符串时间 #time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则显示当前时间 >>>time.strftime("%Y-%m-%d %X") '2017-07-24 14:55:36' >>>time.strftime("%Y-%m-%d",time.localtime(1500000000)) '2017-07-14' #字符串时间-->结构化时间 #time.strptime(时间字符串,字符串对应格式) >>>time.strptime("2017-03-16","%Y-%m-%d") time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) >>>time.strptime("07/24/2017","%m/%d/%Y") time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
#结构化时间 --> %a %b %d %H:%M:%S %Y串 #time.asctime(结构化时间) 若是不传参数,直接返回当前时间的格式化串 >>>time.asctime(time.localtime(1500000000)) 'Fri Jul 14 10:40:00 2017' >>>time.asctime() 'Mon Jul 24 15:18:33 2017' #时间戳 --> %a %b %d %H:%M:%S %Y串 #time.ctime(时间戳) 若是不传参数,直接返回当前时间的格式化串 >>>time.ctime() 'Mon Jul 24 15:19:07 2017' >>>time.ctime(1500000000) 'Fri Jul 14 10:40:00 2017'
# 时间加减 import time import datetime print(datetime.datetime.now()) # 返回 2016-08-19 12:47:03.941925 print(datetime.date.fromtimestamp(time.time())) # 时间戳直接转成日期格式 2016-08-19 print(datetime.datetime.now()) print(datetime.datetime.now() + datetime.timedelta(3)) # 当前时间+3天 print(datetime.datetime.now() + datetime.timedelta(-3)) # 当前时间-3天 print(datetime.datetime.now() + datetime.timedelta(hours=3)) # 当前时间+3小时 print(datetime.datetime.now() + datetime.timedelta(minutes=30)) # 当前时间+30分 c_time = datetime.datetime.now() print(c_time.replace(minute=3, hour=2)) # 时间替换
对于大部分基本的日期和时间操控问题,datatime模块已经足够知足要求了。若是须要处理更为复杂的日期问题,好比处理时区、模糊时间范围、计算节日的日期等,可使用dateutil模块。
dateutil模块可以处理不一样月份的天数。
>>> from datetime import datetime, timedelta >>> a = datetime(2012, 9, 23) >>> a + timedelta(months=1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'months' is an invalid keyword argument for this function >>> from dateutil.relativedelta import relativedelta >>> a + relativedelta(months=+1) datetime.datetime(2012, 10, 23, 0, 0) >>> a + relativedelta(months=+4) datetime.datetime(2013, 1, 23, 0, 0)
>>> import random #随机小数 >>> random.random() # 大于0且小于1之间的小数 0.7664338663654585 >>> random.uniform(1,3) #大于1小于3的小数 1.6270147180533838
#随机整数 >>> random.randint(1,5) # 大于等于1且小于等于5之间的整数 >>> random.randrange(1,10,2) # 大于等于1且小于10之间的奇数 #随机选择一个返回 >>> random.choice([1,'23',[4,5]]) # #1或者23或者[4,5] #随机选择多个返回,返回的个数为函数的第二个参数 >>> random.sample([1,'23',[4,5]],2) # #列表元素任意2个组合 [[4, 5], '23'] #打乱列表顺序 >>> item=[1,3,5,7,9] >>> random.shuffle(item) # 打乱次序 >>> item [5, 1, 3, 7, 9] >>> random.shuffle(item) >>> item [5, 9, 7, 1, 3]
练习:生成随机验证码
import random def v_code(): code = '' for i in range(5): num=random.randint(0,9) alf=chr(random.randint(65,90)) add=random.choice([num,alf]) code="".join([code,str(add)]) return code print(v_code())
os模块是与操做系统交互 的一个接口
os.makedirs('dirname1/dirname2') 可生成多层递归目录 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 os.mkdir('dirname') 生成单级目录;至关于shell中mkdir dirname os.rmdir('dirname') 删除单级空目录,若目录不为空则没法删除,报错;至关于shell中rmdir dirname os.listdir('dirname') 列出指定目录下的全部文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() 删除一个文件 os.rename("oldname","newname") 重命名文件/目录 os.stat('path/filename') 获取文件/目录信息 os.system("bash command") 运行shell命令,直接显示 os.popen("bash command).read() 运行shell命令,获取执行结果 os.getcwd() 获取当前工做目录,即当前python脚本工做的目录路径 os.chdir("dirname") 改变当前脚本工做目录;至关于shell下cd
os.path
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 os.path.exists(path) 若是path存在,返回True;若是path不存在,返回False os.path.isabs(path) 若是path是绝对路径,返回True os.path.isfile(path) 若是path是一个存在的文件,返回True。不然返回False os.path.isdir(path) 若是path是一个存在的目录,则返回True。不然返回False os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径以前的参数将被忽略 os.path.getatime(filename) 返回文件或者目录的最后访问时间 os.path.getmtime(filename) 返回文件或者目录的最后修改时间 os.path.getsize(filename) 返回filename的大小
注意:os.stat ('path/filename' ) 获取文件/ 目录信息 的结构说明
stat 结构: st_mode: inode 保护模式 st_ino: inode 节点号。 st_dev: inode 驻留的设备。 st_nlink: inode 的连接数。 st_uid: 全部者的用户ID。 st_gid: 全部者的组ID。 st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。 st_atime: 上次访问的时间。 st_mtime: 最后一次修改的时间。 st_ctime: 由操做系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是建立时间(详细信息参见平台的文档)。
os.sep 输出操做系统特定的路径分隔符,win下为"\\",Linux下为"/" os.linesep 输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n" os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
sys模块是与 python解释器交互的一个接口
sys.argv 命令行参数List,第一个元素是程序自己路径 sys.exit(n) 退出程序,正常退出时exit(0),错误退出sys.exit(1) sys.version 获取Python解释程序的版本信息 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操做系统平台名称
shutil是一个高级的文件、文件夹、压缩包处理模块。
import shutil # 将文件内容拷贝到另外一个文件中 shutil.copyfileobj(open('src.log'), open('dst.log')) # 拷贝文件 shutil.copyfile('src.log', 'dsc.log') # 仅拷贝权限。内容、组、用户均不变 shutil.copymode('src.log', 'dsc.log') # 目标文件必须存在 # 仅拷贝状态的信息,包括:mode bits, atime, mtime, flags shutil.copystat('src.log', 'dsc.log') # 目标文件必须存在 # 拷贝文件和权限 shutil.copy('src.log', 'dsc.log') # 拷贝文件和状态信息 shutil.copy2('src.log', 'dsc.log') # 递归的去拷贝文件夹 # 目标目录不能存在,注意对folder2目录父级目录要有可写权限 # ignore的意思是排除 # 还支持拷贝软链:symlinks=True shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) # 递归的去删除文件 shutil.rmtree('folder1') # 递归的去移动文件,它相似mv命令,其实就是重命名。 shutil.move('folder1', 'folder3') # 建立压缩包并返回文件路径,例如:zip、tar # 几个参数 # base_name: 压缩包的文件名,也能够是压缩包的路径。只是文件名时,则保存至当前目录,不然保存至指定路径。 # format:压缩包种类,“zip”, “tar”, “bztar”,“gztar” # root_dir:要压缩的文件夹路径(默认当前目录) # owner:用户,默认当前用户 # group:组,默认当前组 # logger:用于记录日志,一般是logging.Logger对象 # 将 /data 下的文件打包放置当前程序目录 ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data') # 将 /data下的文件打包放置 /tmp/目录 ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
import tarfile # 压缩 >>> t=tarfile.open('/tmp/xx.tar','w') >>> t.add('/test1/a.py',arcname='a.bak') >>> t.add('/test1/b.py',arcname='b.bak') >>> t.close() # 解压 >>> t=tarfile.open('/tmp/xx.tar','r') >>> t.extractall('/tmp') >>> t.close()
import zipfile # 压缩 z = zipfile.ZipFile('xx.zip', 'w') z.write('a.log') z.write('data.data') z.close() # 解压 z = zipfile.ZipFile('xx.zip', 'r') z.extractall(path='.') z.close()
序列化(Serialization)是将对象的状态信息转换为能够存储或传输的形式的过程。
例如将Python中的字典、列表等对象转换成一个字符串的过程 。
好比,咱们在python代码中计算的一个数据须要给另一段程序使用,那咱们怎么给? 如今咱们能想到的方法就是存在文件里,而后另外一个python程序再从文件里读出来。 可是咱们都知道,对于文件来讲是没有字典这个概念的,因此咱们只能将数据转换成字典放到文件中。 你必定会问,将字典转换成一个字符串很简单,就是str(dic)就能够办到了,为何咱们还要学习序列化模块呢? 没错序列化的过程就是从dic 变成str(dic)的过程。如今你能够经过str(dic),将一个名为dic的字典转换成一个字符串, 可是你要怎么把一个字符串转换成字典呢? 聪明的你确定想到了eval(),若是咱们将一个字符串类型的字典str_dic传给eval,就会获得一个返回的字典类型了。 eval()函数十分强大,可是eval是作什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。 BUT!强大的函数有代价。安全性是其最大的缺点。 想象一下,若是咱们从文件中读出的不是一个数据结构,而是一句"删除文件"相似的破坏性语句,那么后果实在不堪设设想。 而使用eval就要担这个风险。 因此,咱们并不推荐用eval方法来进行反序列化操做(将str转换成python中的数据结构)
序列化的目的:
若是咱们要在不一样的编程语言之间传递对象,就必须把对象序列化为标准格式,好比XML,但更好的方法是序列化为JSON,由于JSON表示出来就是一个字符串,能够被全部语言读取,也能够方便地存储到磁盘或者经过网络传输。JSON不只是标准格式,而且比XML更快,并且能够直接在Web页面中读取,很是方便。
Python中的json模块提供了四个功能:dumps、dump、loads、load
import json dic = {'k1':'v1','k2':'v2','k3':'v3'} str_dic = json.dumps(dic) #序列化:将一个字典转换成一个字符串 print(type(str_dic),str_dic) #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"} #注意,json转换完的字符串类型的字典中的字符串是由""表示的 dic2 = json.loads(str_dic) #反序列化:将一个字符串格式的字典转换成一个字典 #注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示 print(type(dic2),dic2) #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}] str_dic = json.dumps(list_dic) #也能够处理嵌套的数据类型 print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}] list_dic2 = json.loads(str_dic) print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
import json f = open('json_file','w') dic = {'k1':'v1','k2':'v2','k3':'v3'} json.dump(dic,f) #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件 f.close() f = open('json_file') dic2 = json.load(f) #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回 f.close() print(type(dic2),dic2)
import json f = open('file','w') json.dump({'国籍':'中国'},f) ret = json.dumps({'国籍':'中国'}) f.write(ret+'\n') json.dump({'国籍':'美国'},f,ensure_ascii=False) ret = json.dumps({'国籍':'美国'},ensure_ascii=False) f.write(ret+'\n') f.close()
Serialize obj to a JSON formatted str.(字符串表示的json对象) Skipkeys:默认值是False,若是dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key ensure_ascii:,当它为True的时候,全部非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False便可,此时存入json的中文便可正常显示。) If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse). If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity). indent:应该是一个非负的整型,若是是0就是顶格分行显示,若是为空就是一行最紧凑显示,不然会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json separators:分隔符,其实是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。 default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. sort_keys:将数据根据keys的值进行排序。 To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
import json data = {'username':['李华','二愣子'],'sex':'male','age':16} json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False) print(json_dic2)
注意:json模块序列化以后获得的字符串中都是双引号。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应以下:
首先咱们来看一下pickle和json的一个区别:
pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不只能够序列化字典,列表... 能够把python中任意的数据类型序列化 )
import pickle dic = {'k1':'v1','k2':'v2','k3':'v3'} str_dic = pickle.dumps(dic) print(str_dic) #一串二进制内容 dic2 = pickle.loads(str_dic) print(dic2) #字典 import time struct_time = time.localtime(1000000000) print(struct_time) f = open('pickle_file','wb') pickle.dump(struct_time,f) f.close() f = open('pickle_file','rb') struct_time2 = pickle.load(f) print(struct_time2.tm_year)
这时候机智的你又要说了,既然pickle如此强大,为何还要学json呢?
这里咱们要说明一下,json是一种全部的语言均可以识别的数据结构。
若是咱们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也能够拿来用。
可是若是咱们用pickle进行序列化,其余语言就不能读懂这是什么了~
因此,若是你序列化的内容是列表或者字典,咱们很是推荐你使用json模块
但若是出于某种缘由你不得不序列化其余的数据类型,而将来你还会用python对这个数据进行反序列化的话,那么就可使用pickle
讲正题以前咱们先来看一个例子: https://reg.jd.com/reg/person?ReturnUrl=https%3A//www.jd.com/
这是京东的注册页面,打开页面咱们就看到这些要求输入我的信息的提示。
假如咱们随意的在手机号码这一栏输入一个11111111111,它会提示咱们格式有误。
这个功能是怎么实现的呢?
假如如今你用python写一段代码,相似:
phone_number = input('please input your phone number : ')
你怎么判断这个phone_number是合法的呢?
根据手机号码一共11位而且是只以1三、1四、1五、18开头的数字这些特色,咱们用python写了以下代码:
while True: phone_number = input('please input your phone number : ') if len(phone_number) == 11 \ and phone_number.isdigit()\ and (phone_number.startswith('13') \ or phone_number.startswith('14') \ or phone_number.startswith('15') \ or phone_number.startswith('18')): print('是合法的手机号码') else: print('不是合法的手机号码')
这是你的写法,如今我要展现一下个人写法:
import re phone_number = input('please input your phone number : ') if re.match('^(13|14|15|18)[0-9]{9}$',phone_number): print('是合法的手机号码') else: print('不是合法的手机号码')
对比上面的两种写法,此时此刻,我要问你你喜欢哪一种方法呀?你确定仍是会说第一种,为何呢?由于第一种不用学呀!
可是若是如今有一个文件,我让你从整个文件里匹配出全部的手机号码。你用python给我写个试试?
可是学了今天的技能以后,分分钟帮你搞定!
今天咱们要学习python里的 re模块和正则表达式 ,学会了这个就能够帮咱们解决刚刚的疑问。正则表达式不只在python领域,在整个编程届都占有举足轻重的地位。
无论之后你是否是去作python开发,只要你是一个程序员就应该了解正则表达式的基本使用。若是将来你要在爬虫领域发展,你就更应该好好学习这方面的知识。 可是你要知道,re模块本质上和正则表达式没有一毛钱的关系。re模块和正则表达式的关系 相似于 time模块和时间的关系 你没有学习python以前,也不知道有一个time模块,可是你已经认识时间了 12:30就表示中午十二点半(这个时间可好,通常这会儿就该下课了)。 时间有本身的格式,年月日时分秒,12个月,365天......已经成为了一种规则。你也早就牢记于心了。time模块只不过是python提供给咱们的能够方便咱们操做时间的一个工具而已
正则表达式 自己也和python没有什么关系,就是 匹配字符串内容的一种规则 。
官方定义:正则表达式是对字符串操做的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
一说规则我已经知道你很晕了,如今就让咱们先来看一些实际的应用。 在线测试工具 http://tool.chinaz.com/regex/
首先你要知道的是,谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每个字都是一个字符串。
其次,若是在一个位置的一个值,不会出现什么变化,那么是不须要规则的。
好比你要用"1"去匹配"1",或者用"2"去匹配"2",直接就能够匹配上。这连python的字符串操做均可以轻松作到。
那么在以后咱们更多要考虑的是在同一个位置上能够出现的字符的范围。
字符组 : [字符组] 在同一个位置可能出现的各类字符组成了一个字符组,在正则表达式中用[]表示 字符分为不少类,好比数字、字母、标点等等。 假如你如今要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、一、2...9这10个数之一。
正则 |
待匹配字符 |
匹配 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的全部字符,字符组里的任意一个字符 |
[0123456789] |
a |
False |
因为字符组中没有"a"字符,因此不能匹配 |
[0-9] |
7 |
True |
也能够用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
一样的若是要匹配全部的小写字母,直接用[a-z]就能够表示 |
[A-Z] |
B |
True |
[A-Z]就表示全部的大写字母 |
[0-9a-fA-F] |
e |
True |
能够匹配数字,大小写形式的a~f,用来验证十六进制字符 |
字符:
元字符 |
匹配内容 |
. | 匹配除换行符之外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符a或字符b |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的全部字符 |
量词:
量词 |
用法说明 |
* | 重复零次或更屡次 |
+ | 重复一次或更屡次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更屡次 |
{n,m} | 重复n到m次 |
正则 | 待匹配字符 | 匹配 结果 |
说明 |
海. | 海燕海娇海东 | 海燕海娇海东 | 匹配全部"海."的字符 |
^海. | 海燕海娇海东 | 海燕 | 只从开头匹配"海." |
海.$ | 海燕海娇海东 | 海东 | 只匹配结尾的"海.$" |
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.? | 李杰和李莲英和李二棍子 | 李杰 |
?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 | *表示重复零次或屡次,即匹配"李"后面0或多个任意字符 |
李.+ | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 | +表示重复一次或屡次,即只匹配"李"后面1个或多个任意字符 |
李.{1,2} | 李杰和李莲英和李二棍子 | 李杰和 |
{1,2}匹配1到2次任意字符 |
注意:前面的*,+,?等都是贪婪匹配,也就是尽量匹配,后面加?号使其变成惰性匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.*? | 李杰和李莲英和李二棍子 | 李 李 李 |
惰性匹配 |
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李[杰莲英二棍子]* | 李杰和李莲英和李二棍子 | 李杰 |
表示匹配"李"字后面[杰莲英二棍子]的字符任意次 |
李[^和]* | 李杰和李莲英和李二棍子 | 李杰 |
表示匹配一个不是"和"的字符任意次 |
[\d] | 456bdha3 | 4 |
表示匹配任意一个数字,匹配到4个结果 |
[\d]+ | 456bdha3 | 456 |
表示匹配任意个数字,匹配到2个结果 |
身份证号码是一个长度为15或18个字符的字符串,若是是15位则所有由数字组成,首位不能为0;若是是18位,则前17位所有是数字,末位多是数字或x,下面咱们尝试用正则来表示:
正则 | 待匹配字符 | 匹配 结果 |
说明 |
^[1-9]\d{13,16}[0-9x]$ | 110101198001017032 | 110101198001017032 |
表示能够匹配一个正确的身份证号 |
^[1-9]\d{13,16}[0-9x]$ | 1101011980010170 | 1101011980010170 |
表示也能够匹配这串数字,但这并非一个正确的身份证号码,它是一个16位的数字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ | 1101011980010170 | False |
如今不会匹配错误的身份证号了 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 110105199812067023 | 110105199812067023 |
表示先匹配[1-9]\d{16}[0-9x]若是没有匹配上就匹配[1-9]\d{14} |
在正则表达式中,有不少有特殊意义的是元字符,好比\n和\s等,若是要在正则中匹配正常的"\n"而不是"换行符"就须要对"\"进行转义,变成'\\'。
在python中,不管是正则表达式,仍是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,自己还须要转义。因此若是匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候咱们就用到了r'\n'这个概念,此时的正则是r'\\n'就能够了。
正则 | 待匹配字符 | 匹配 结果 |
说明 |
\n | \n | False | 由于在正则表达式中\是有特殊意义的字符,因此要匹配\n自己,用表达式\n没法匹配 |
\\n | \n | True | 转义\以后变成\\,便可匹配 |
"\\\\n" | '\\n' | True | 若是在python中,字符串中的'\'也须要转义,因此每个字符串'\'又须要转义一次 |
r'\\n' | r'\n' | True | 在字符串以前加r,让整个字符串不转义 |
贪婪匹配:在知足匹配时,匹配尽量长的字符串,默认状况下,采用贪婪匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
<.*> | <script>...<script> |
<script>...<script> | 默认为贪婪匹配模式,会匹配尽可能长的字符串 |
<.*?> | <script>...<script> | <script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽可能短的字符串 |
几个经常使用的非贪婪匹配Pattern
*? 重复任意次,但尽量少重复 +? 重复1次或更屡次,但尽量少重复 ?? 重复0次或1次,但尽量少重复 {n,m}? 重复n到m次,但尽量少重复 {n,}? 重复n次以上,但尽量少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 何在一块儿就是 取尽可能少的任意字符,通常不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现
import re ret = re.findall('a', 'eva egon yuan') # 返回全部知足匹配条件的结果,放在列表里 print(ret) #结果 : ['a', 'a'] ret = re.search('a', 'eva egon yuan').group() print(ret) #结果 : 'a' # 函数会在字符串内查找模式匹配,只到找到第一个匹配而后返回一个包含匹配信息的对象,该对象能够 # 经过调用group()方法获得匹配的字符串,若是字符串没有匹配,则返回None。 ret = re.match('a', 'abc').group() # 同search,不过仅在字符串开始处进行匹配 print(ret) #结果 : 'a' ret = re.split('[ab]', 'abcd') # 先按'a'分割获得''和'bcd',在对''和'bcd'分别按'b'分割 print(ret) # ['', '', 'cd'] ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个 print(ret) #evaHegon4yuan4 ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次) print(ret) obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字 ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串 print(ret.group()) #结果 : 123 import re ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器 print(ret) # <callable_iterator object at 0x10195f940> print(next(ret).group()) #查看第一个结果 print(next(ret).group()) #查看第二个结果 print([i.group() for i in ret]) #查看剩余的左右结果
注意:
1 findall的优先级查询:
import re ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['oldboy'] 这是由于findall会优先把匹配结果组里内容返回,若是想要匹配结果,取消权限便可 ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['www.oldboy.com']
2 split的优先级查询
ret=re.split("\d+","eva3egon4yuan") print(ret) #结果 : ['eva', 'egon', 'yuan'] ret=re.split("(\d+)","eva3egon4yuan") print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan'] #在匹配部分加上()以后所切出的结果是不一样的, #没有()的没有保留所匹配的项,可是有()的却可以保留了匹配的项, #这个在某些须要保留匹配部分的使用过程是很是重要的。
import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>") #还能够在分组中利用?<name>的形式给分组起名字 #获取的匹配结果能够直接用group('名字')拿到对应的值 print(ret.group('tag_name')) #结果 :h1 print(ret.group()) #结果 :<h1>hello</h1> ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>") #若是不给组起名字,也能够用\序号来找到对应的组,表示要找的内容和前面的组内容一致 #获取的匹配结果能够直接用group(序号)拿到对应的值 print(ret.group(1)) print(ret.group()) #结果 :<h1>hello</h1>
import re ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))") print(ret) #['1', '2', '60', '40', '35', '5', '4', '3'] ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))") print(ret) #['1', '-2', '60', '', '5', '-4', '3'] ret.remove("") print(ret) #['1', '-2', '60', '5', '-4', '3']
1、 匹配一段文本中的每行的邮箱 http://blog.csdn.net/make164492212/article/details/51656638 二、 匹配一段文本中的每行的时间字符串,好比:‘1990-07-12’; 分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、 一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 三、 匹配qq号。(腾讯QQ号从10000开始) [1,9][0,9]{4,} 四、 匹配一个浮点数。 ^(-?\d+)(\.\d+)?$ 或者 -?\d+\.?\d* 五、 匹配汉字。 ^[\u4e00-\u9fa5]{0,}$ 六、 匹配出全部整数
import requests import re import json def getPage(url): response=requests.get(url) return response.text def parsePage(s): com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',re.S) ret=com.finditer(s) for i in ret: yield { "id":i.group("id"), "title":i.group("title"), "rating_num":i.group("rating_num"), "comment_num":i.group("comment_num"), } def main(num): url='https://movie.douban.com/top250?start=%s&filter='%num response_html=getPage(url) ret=parsePage(response_html) print(ret) f=open("move_info7","a",encoding="utf8") for obj in ret: print(obj) data=json.dumps(obj,ensure_ascii=False) f.write(data+"\n") if __name__ == '__main__': count=0 for i in range(10): main(count) count+=25
import re import json from urllib.request import urlopen def getPage(url): response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): com = re.compile( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S) ret = com.finditer(s) for i in ret: yield { "id": i.group("id"), "title": i.group("title"), "rating_num": i.group("rating_num"), "comment_num": i.group("comment_num"), } def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num response_html = getPage(url) ret = parsePage(response_html) print(ret) f = open("move_info7", "a", encoding="utf8") for obj in ret: print(obj) data = str(obj) f.write(data + "\n") count = 0 for i in range(10): main(count) count += 25
flags有不少可选值: re.I(IGNORECASE)忽略大小写,括号内是完整的写法 re.M(MULTILINE)多行模式,改变^和$的行为 re.S(DOTALL)点能够匹配任意字符,包括换行符 re.L(LOCALE)作本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用 re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag re.X(VERBOSE)冗长模式,该模式下pattern字符串能够是多行的,忽略空白字符,并能够添加注释
实现能计算相似 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等相似公式的计算器程序
在线测试工具 http://tool.chinaz.com/regex/
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它经过一个函数,把任意长度的数据转换为一个长度固定的数据串(一般用16进制的字符串表示)。
摘要算法就是经过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。
摘要算法之因此能指出数据是否被篡改过,就是由于摘要函数是一个单向函数,计算f(data)很容易,但经过digest反推data却很是困难。并且,对原始数据作一个bit的修改,都会致使计算出的摘要彻底不一样。
咱们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:
import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?') print md5.hexdigest() 计算结果以下: d26a53750bc40b38b65a520292f69306
若是数据量很大,能够分块屡次调用update(),最后计算的结果是同样的:
md5 = hashlib.md5() md5.update('how to use md5 in ') md5.update('python hashlib?') print md5.hexdigest()
MD5是最多见的摘要算法,速度很快,生成结果是固定的128 bit字节,一般用一个32位的16进制字符串表示。另外一种常见的摘要算法是SHA1,调用SHA1和调用MD5彻底相似:
import hashlib sha1 = hashlib.sha1() sha1.update('how to use sha1 in ') sha1.update('python hashlib?') print sha1.hexdigest()
SHA1的结果是160 bit字节,一般用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,并且摘要长度更长。
任何容许用户登陆的网站都会存储用户登陆的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:
name | password --------+---------- michael | 123456 bob | abc999 alice | alice2008
若是以明文保存用户口令,若是数据库泄露,全部用户的口令就落入黑客的手里。此外,网站运维人员是能够访问数据库的,也就是能获取到全部用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,好比MD5:
username | password ---------+--------------------------------- michael | e10adc3949ba59abbe56e057f20f883e bob | 878ef96e86145580c38c87f0410ad153 alice | 99b1c2188db85afee403b1536010c2c9
考虑这么个状况,不少用户喜欢用123456,888888,password这些简单的口令,因而,黑客能够事先计算出这些经常使用口令的MD5值,获得一个反推表:
'e10adc3949ba59abbe56e057f20f883e': '123456' '21218cca77804d2ba1922c33e0151105': '888888' '5f4dcc3b5aa765d61d8327deb882cf99': 'password'
这样,无需破解,只须要对比数据库的MD5,黑客就得到了使用经常使用口令的用户帐号。
对于用户来说,固然不要使用过于简单的口令。可是,咱们可否在程序设计上对简单口令增强保护呢?
因为经常使用口令的MD5值很容易被计算出来,因此,要确保存储的用户口令不是那些已经被计算出来的经常使用口令的MD5,这一方法经过对原始口令加一个复杂字符串来实现,俗称“加盐”:
hashlib.md5("salt".encode("utf8"))
通过Salt处理的MD5口令,只要Salt不被黑客知道,即便用户输入简单口令,也很难经过MD5反推明文口令。
可是若是有两个用户都使用了相同的简单口令好比123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是同样的。有没有办法让使用相同口令的用户存储不一样的MD5呢?
若是假定用户没法修改登陆名,就能够经过把登陆名做为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不一样的MD5。
摘要算法在不少地方都有普遍的应用。要注意摘要算法不是加密算法,不能用于加密(由于没法经过摘要反推明文),只能用于防篡改,可是它的单向计算特性决定了能够在不存储明文口令的状况下验证用户口令。
该模块适用于配置文件的格式与windows ini文件相似,能够包含一个或多个节(section),每一个节能够有多个参数(键=值)。
来看一个好多软件的常见文档格式以下:
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
若是想用python生成一个这样的文档怎么作呢?
import configparser config = configparser.ConfigParser() config["DEFAULT"] = {'ServerAliveInterval': '45', 'Compression': 'yes', 'CompressionLevel': '9', 'ForwardX11':'yes' } config['bitbucket.org'] = {'User':'hg'} config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'} with open('example.ini', 'w') as configfile: config.write(configfile)
import configparser config = configparser.ConfigParser() #---------------------------查找文件内容,基于字典的形式 print(config.sections()) # [] config.read('example.ini') print(config.sections()) # ['bitbucket.org', 'topsecret.server.com'] print('bytebong.com' in config) # False print('bitbucket.org' in config) # True print(config['bitbucket.org']["user"]) # hg print(config['DEFAULT']['Compression']) #yes print(config['topsecret.server.com']['ForwardX11']) #no print(config['bitbucket.org']) #<Section: bitbucket.org> for key in config['bitbucket.org']: # 注意,有default会默认default的键 print(key) print(config.options('bitbucket.org')) # 同for循环,找到'bitbucket.org'下全部键 print(config.items('bitbucket.org')) #找到'bitbucket.org'下全部键值对 print(config.get('bitbucket.org','compression')) # yes get方法Section下的key对应的value
import configparser config = configparser.ConfigParser() config.read('example.ini') config.add_section('yuan') config.remove_section('bitbucket.org') config.remove_option('topsecret.server.com',"forwardx11") config.set('topsecret.server.com','k1','11111') config.set('yuan','k2','22222') config.write(open('new2.ini', "w"))
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
默认状况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。
灵活配置日志级别,日志格式,输出位置:
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='/tmp/test.log', filemode='w') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
配置参数:
import logging logger = logging.getLogger() # 建立一个handler,用于写入日志文件 fh = logging.FileHandler('test.log',encoding='utf-8')
# 再建立一个handler,用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger对象能够添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
logging库提供了多个组件:Logger、Handler、Filter、Formatter。
Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。
另外,能够经过:logger.setLevel(logging.Debug)设置级别,固然也能够经过fh.setLevel(logging.Debug)单独对某个日志handler设置级别。