python基础(十七)
今日主要内容
- 正则表达式
- logging模块
一、正则表达式
(一)什么是正则表达式
- 正则表达式的定义:
- 是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
- 简单来说,我们使用正则表达式就是为了对字符串进行 匹配 和 过滤
- 正则表达式的特点:
- 灵活性强、逻辑性强、功能性强
- 可以迅速的用极简单的方式控制复杂的字符串
(二)正则表达式的构成
- 正则表达式由普通字符和元字符构成
- 普通字符
- 元字符
-
普通字符
-
普通字符包括:大小写字母、数字,在匹配普通字符的时候直接写就可以了,Python也能实现同样的效果,很简单
- 利用
re
模块,返回正则表达式匹配过滤出来的字符
import re s = "zxdnbzxdnb" print(re.findall("zxd", s)) # 普通字符,直接写就好了 运行结果: ['zxd', 'zxd'] # 成功匹配过滤
- Python基础逻辑也可以实现
s = "zxdnbzxdnb" print("zxd" in s) # 是否存在 print(s.count("zxd")) # 存在几个 print(["zxd"] * 2) # 打印出来就好了 运行结果: True 2 ['zxd', 'zxd'] # 实现同样的效果
- 利用
-
-
元字符
-
元字符才是正则表达式的灵魂所在,介绍部分内容:
- 字符组
- 常用元字符(单一匹配)
- 量词
-
字符组
-
字符组用
[]
表示,[]
中出现的内容会被匹配到例:匹配到字符串
"zxdhnbzxdznbzxdtnb"
中的"hnb"
"znb"
"tnb"
import re s = "zxdhnbzxdznbzxdtnb" print(re.findall("[hzt]nb", s)) 运行结果: ['hnb', 'znb', 'tnb'] # 将字符串中含有"hnb"、"znb"、"tnb"的部分过滤出来
-
[]
还可填入一个范围,比如[0-9a-zA-Z]
它可以匹配字符串中0到9,a到z,A到Z中的字符例:匹配到字符串
"zxd123456zxdznb"
中的数字import re s = "zxd123456zxdznb" print(re.findall("[0-9]", s)) 运行结果: ['1', '2', '3', '4', '5', '6'] # 匹配到字符串中的0到9的数字
-
范围遵循ascii码的从小到大规定,比如
[A-z]
可以匹配之间的所有字符,但[a-Z]
只能匹配到aimport re s = "asdzxcqwe123!@#" print(re.findall("[A-z]", s)) print(re.findall("[a-Z]", s)) 运行结果: ['a', 's', 'd', 'z', 'x', 'c', 'q', 'w', 'e'] # [A-z]匹配成功 sre_constants.error: bad character range a-Z at position 1 # [a-Z]报错
-
字符组还可以反向过滤,在
[]
中加入^
,比如[^123]
它可以匹配字符串中除了123以外的所有字符例:匹配到字符串
"zxdznb123456789"
中除数字以外的字符import re s = "zxdznb123456789" print(re.findall("[^0-9]", s)) 运行结果: ['z', 'x', 'd', 'z', 'n', 'b'] # 匹配到字符串中除数字以外的字符
-
应用举例:通过匹配判断手机号输入是否正确(有点low的例子)
import re number = input("手机号:") if len(number) != 11: print("手机号输入有误!") else: num = re.findall("[1][3-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]", number) if not num: print("手机号格式不正确!") else: print("手机号可以使用!")
-
-
常用元字符(单一匹配)
\w 匹配字母(含中文)、数字、下划线 \W 匹配非字母(含中文)、数字、下划线 \s 匹配空白符 \S 匹配非空白符 \d 匹配数字 \D 匹配非数字 \b 匹配单词的边界 \B 匹配非单词的边界 \A 匹配字符串的开始 ^ 匹配字符串的开始 \Z 匹配字符串的结尾 $ 匹配字符串的结尾 \n 匹配一个换行符 \t 匹配一个制表符 . 匹配除换行符以外任意字符 a|b 匹配字符a或b () 分组,匹配括号内的表达式 [] 匹配字符组内的字符 [^] 匹配除字符组以外的字符 -
\w :匹配字符串中字母(含中文)、数字、下划线
例:匹配到字符串
"zxd123\n456!@#¥"
中的字母和数字import re s = "zxd123\n456!@#¥" print(re.findall("\w", s)) 运行结果: ['z', 'x', 'd', '1', '2', '3', '4', '5', '6']
-
\W :匹配字符串中非字母(含中文)、数字、下划线
例:匹配到字符串
"zxd123\n456!@#¥"
中的非字母和数字的内容import re s = "zxd123\n456!@#¥" print(re.findall("\W", s)) 运行结果: ['\n', '!', '@', '#', '¥']
-
\s :匹配空白符(包含空格、制表符和换行符 )
例:匹配到字符串
"zxd \t123 \n456 !@#¥"
中的空白符import re s = "zxd \t123 \n456 !@#¥" print(re.findall("\s", s)) 运行结果: [' ', ' ', '\t', ' ', ' ', '\n', ' ', ' ']
-
\S :匹配非空白符
例:匹配到字符串
"zxd \t123 \n456 !@#¥"
中非空白符的内容import re s = "zxd \t123 \n456 !@#¥" print(re.findall("\S", s)) 运行结果: ['z', 'x', 'd', '1', '2', '3', '4', '5', '6', '!', '@', '#', '¥']
-
\d :匹配数字
例:匹配到字符串
"zxd123456zxdznb"
中的数字import re s = "zxd123456zxdznb" print(re.findall("\d", s)) 运行结果: ['1', '2', '3', '4', '5', '6']
-
\D :匹配非数字
例:匹配到字符串
"zxd123456zxdznb"
中的非数字的内容import re s = "zxd123456zxdznb" print(re.findall("\D", s)) 运行结果: ['z', 'x', 'd', 'z', 'x', 'd', 'z', 'n', 'b']
-
\b :匹配单词边界
例:匹配到字符串
"周杰伦-麻花藤-潘长江-赵本山-林俊周-韩周鸿-周猩猩-周星"
中所有姓周的人名import re s = "周杰伦-麻花藤-潘长江-赵本山-林俊周-韩周鸿-周猩猩-周星" print(re.findall(r"\b周\w+", s)) 运行结果: ['周杰伦', '周猩猩', '周星']
-
\B :匹配非单词边界
例:匹配到字符串
"周杰伦-麻花藤-潘长江-赵本山-林俊周-韩周鸿-周猩猩-周星"
中所有名带有周的人import re s = "周杰伦-麻花藤-潘长江-赵本山-林俊周-韩周鸿-周猩猩-周星" print(re.findall(r"\w+\B周\w*", s)) 运行结果: ['林俊周', '韩周鸿']
-
\A :匹配字符串的开头(不常用,知道就行)
例:匹配到字符串
"zxdhnbzxdznb"
的开头import re s = "zxdhnbzxdznb" print(re.findall("\A\w", s)) 运行结果: ['z']
-
^ :匹配字符串的开头(常用)
例:匹配到字符串
"zxdhnbzxdznb"
的开头import re s = "zxdhnbzxdznb" print(re.findall("^\w", s)) 运行结果: ['z']
-
\Z :匹配字符串的结尾(不常用,知道就行)
例:匹配到字符串
"zxdhnbzxdznb"
的结尾import re s = "zxdhnbzxdznb" print(re.findall("\w\Z", s)) 运行结果: ['b']
-
$ :匹配字符串的结尾(常用)
例:匹配到字符串
"zxdhnbzxdznb"
的结尾import re s = "zxdhnbzxdznb" print(re.findall("\w$", s)) 运行结果: ['b']
-
^ 和 $ 结合使用时,两者之间的长度一定要和待匹配的字符串长度相同,否则匹配不上
import re s = "zxdznb666" print(re.findall("^zxdznb666$", s)) print(re.findall("^zxdznb$", s)) 运行结果: ['zxdznb666'] []
-
^ 和 $ 的应用举例:通过匹配判断手机号输入是否正确(升级版,还是很low)
import re number = input("手机号:") num = re.findall("^[1][3-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$", number) # 匹配到以1开头,以0到9结尾的一串11位数字 if not num: print("手机号格式不正确!") else: print("手机号可以使用!")
-
\n :匹配换行符
例:匹配到字符串
"zxd \t123 \n456 !@#$"
中的换行符import re s = "zxd \t123 \n456 !@#$" print(re.findall("\n", s)) 运行结果: ['\n']
-
\t :匹配制表符
例:匹配到字符串
"zxd \t123 \n456 !@#$"
中的制表符import re s = "zxd \t123 \n456 !@#$" print(re.findall("\t", s)) 运行结果: ['\t']
-
. :匹配除换行符任意的字符,当指定
re.DOTALL
时,可以匹配含换行符以内的任意字符例:匹配字符串
"zxd123\n456"
中所有内容(含换行符|不含换行符)import re s = "zxd123\n456" print(re.findall(".", s)) print(re.findall(".", s, re.DOTALL)) 运行结果: ['z', 'x', 'd', '1', '2', '3', '4', '5', '6'] ['z', 'x', 'd', '1', '2', '3', '\n', '4', '5', '6']
-
a|b :匹配字符a或b,优先匹配前面,只要前面匹配到了后面就不看了,继续向下匹配
例:匹配到字符串
"zxdnb666zxd"
中的zxd
和666
import re s = "zxdnb666zxd" print(re.findall("zxd|666", s)) 运行结果: ['zxd', '666', 'zxd']
-
() :分组,匹配括号内的表达式,可以在括号两端增加限制
例:匹配到字符串
"zxdnb66zxd66zxd"
中的zxd
,只匹配到66中间的zxd
import re s = "zxdnb66zxd66zxd" print(re.findall("(zxd)", s)) print(re.findall("66(zxd)66", s)) 运行结果: ['zxd', 'zxd', 'zxd'] ['zxd']
-
[] :匹配字符组范围内的字符
-
[^] :匹配非字符组范围内的字符
-
-
量词(多个匹配)
***** 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 - ***** :重复零次或更多次
import re s = "zxdnb66zxd66zxd" print(re.findall("6*", s)) 运行结果: ['', '', '', '', '', '66', '', '', '', '66', '', '', '', '']
- + :重复一次或更多次
import re s = "zxdnb66zxd66zxd" print(re.findall("6+", s)) 运行结果: ['66', '66']
- ?:重复零次或一次
import re s = "zxdnb66zxd66zxd" print(re.findall("6?", s)) 运行结果: ['', '', '', '', '', '6', '6', '', '', '', '6', '6', '', '', '', '']
- {n} :重复n次
import re s = "zxdnb66zxd66zxd" print(re.findall("\w{3}", s)) 运行结果: ['zxd', 'nb6', '6zx', 'd66', 'zxd']
- {n,} :重复n次或更多次
import re s = "zxdnb66-zxd-6-6zxd" print(re.findall("\w{3,}", s)) 运行结果: ['zxdnb66', 'zxd', '6zxd']
- {n,m} :重复n到m次
import re s = "zxdnb66-zxd-6-6zxd" print(re.findall("\w{3,5}", s)) 运行结果: ['zxdnb', 'zxd', '6zxd']
- {} 的应用举例:通过匹配判断手机号输入是否正确(再次升级版)
import re number = input("手机号:") num = re.findall("^[1][3-9][0-9]{9}$", number) # 匹配到以1开头,以0到9结尾的一串11位数字 if not num: print("手机号格式不正确!") else: print("手机号可以使用!")
-
-
贪婪匹配和非贪婪匹配(惰性匹配)
-
贪婪匹配:尽可能多的匹配,量词中的
* + {}
都是贪婪匹配import re s = "zxdznb666" print(re.findall("\w*", s)) print(re.findall("\w+", s)) print(re.findall("\w{1,3}", s)) 运行结果: ['zxdznb666', ''] ['zxdznb666'] ['zxd', 'znb', '666']
- 贪婪匹配底层运用到的算法:回溯算法
-
非贪婪匹配(惰性匹配):尽可能少的匹配,量词中的
?
是非贪婪匹配import re s = "zxdznb666" print(re.findall("\w{1,3}?", s)) 运行结果: ['z', 'x', 'd', 'z', 'n', 'b', '6', '6', '6']
- 非贪婪匹配底层用到的算法:先去找结束的值,再去判断规则
-
-
转义:定义规则时,会遇到类似于\n这样的具有特殊意义的词,可以使用转义表示,转义有两种方式:
- 方式一:
\\n
- 方式二:
r"\n"
- 方式一:
(三)正则模块re
-
核心方法:
re.findall() 按规则查找,返回list re,finditer() 按规则查找,返回一个迭代器,通过group方法取值 re.search() 任意位置匹配,匹配到就返回结果,没匹配上返回None re.match() 只能从字符串开头开始匹配,匹配到就返回结果,匹配不到返回None -
re.findall()
- 函数定义:
findall(pattern, string, flags=0)
- 函数说明:按规则查找,返回一个列表
import re s = "zxd123\n456!@#¥" print(re.findall("\w", s)) 运行结果: ['z', 'x', 'd', '1', '2', '3', '4', '5', '6']
- 函数定义:
-
re,finditer()
- 函数定义:
finditer(pattern, string, flags=0)
- 函数说明:按规则查找,返回一个迭代器,通过group方法取值
import re s = "zxd" it = re.finditer("\w", s) for el in it: print(el.group()) 运行结果: z x d
- 函数定义:
-
re.search()
- 函数定义:
search(pattern, string, flags=0)
- 函数说明:任意位置匹配,匹配到就返回结果,没匹配上返回None
import re s = "zxdnb66-zxd-6-6zxd" print(re.search(r"\w+", s).group()) 运行结果: zxdnb66
- 还可以给分组起名称,利用名称输出
- 格式为:
?P<名称>
- 格式为:
import re s = "<h1>hello</h1>" ret = re.search("<(?P<h>\w+)>(?P<h1>\w+)</(?P<h2>\w+)>",s) print(ret.group("h")) print(ret.group("h1")) print(ret.group("h2")) 运行结果: h1 hello h1
- 函数定义:
-
re.match()
- 函数定义:
match(pattern, string, flags=0)
- 函数说明:只能从字符串开头开始匹配,匹配到就返回结果,匹配不到返回None
import re s = "zxdnb66-zxd-6-6zxd" print(re.match(r"\w+", s).group()) s = "-zxdnb66-zxd-6-6zxd" print(re.match(r"\w+", s)) 运行结果: zxdnb66 None
- 函数定义:
-
-
其他方法
re.split() 可按照任意分隔符进行分隔 re.sub() 替换 re.compile() 编译规则 -
re.split()
- 函数定义:
split(pattern, string, maxsplit=0, flags=0)
- 函数说明:按照填入的参数进行切割,返回一个列表,将填入的参数用括号括起来,则在返回的列表中保留切割参数,还可指定最大切割次数,剩余部分作为最后一个元素存放在列表中
import re s = "zxd-nb-666-777-888" print(re.split("-", s)) print(re.split("(-)", s)) print(re.split("-", s, maxsplit=2)) 运行结果: ['zxd', 'nb', '666', '777', '888'] ['zxd', '-', 'nb', '-', '666', '-', '777', '-', '888'] ['zxd', 'nb', '666-777-888']
- 函数定义:
-
re.sub()
- 函数定义:
sub(pattern, repl, string, count=0, flags=0)
- 函数说明:替换,可以通过count控制替换次数
import re s = "zxd-nb-666-777-888" print(re.sub("-", "|", s)) print(re.sub("-", "|", s, 2)) 运行结果: zxd|nb|666|777|888 zxd|nb|666-777-888
- 函数定义:
-
re.compile()
- 函数定义:
compile(pattern, flags=0)
- 函数说明:编译一个正则表达式,返回一个规则对象
import re s = "zxd-nb-666-777-888" obj = re.compile("\w+") print(obj.findall(s)) 运行结果: ['zxd', 'nb', '666', '777', '888']
- 函数定义:
-
二、logging模块
(一)什么是日志
- 我们在编写代码的时候,会报很多的错误,错误信息是不会自动生成的,这些错误信息是编写python解释器的程序员们预先写好的日志加入到程序中,如过触发了某种条件会显示到中控台上,我们在写程序的过程中也会不断调试,我们调试的方法一般都是使用
print
打印出来,这就可以通过日志显示和记录 - 在python中使用logging模块就可以自定义日志,在我们编写程序的时候,就可以利用自定义日志来进行显示和记录
- 最重要的一点,日志是给程序员看的,需要给用户看的还是需要print出来
(二)logging模块
-
logging模块的使用:
- 基础配置型:简单、可定制化差
- 对象配置型:复杂、可定制化强
-
日志分级
日志分级 对应解释 级别数值 debug 调试 10 info 信息 20 warning 警告 30 error 错误 40 critical 危险 50 -
函数式简单配置
- 基础版日志
import logging logging.debug('debug message') # 调试 logging.info('info message') # 信息 logging.warning('warning message') # 警告 logging.error('error message') # 错误 logging.critical('critical message') # 危险 运行结果: WARNING:root:warning message ERROR:root:error message CRITICAL:root:critical message
-
说明:
- 运行时显示的的结果只有
warning
、error
、critical
三个级别的日志信息,logging模块默认从warning
级别(30)的日志开始输出 - 输出的日志由日志级别、用户名、日志信息三部分构成
- 运行时显示的的结果只有
-
通过日志配置函数
logging.basicConfig()
可以调整输出级别、日志内容等信息,配置函数如下:- 函数定义:
basicConfig(**kwargs)
- 关键字参数说明:
filename 指定文件,日志内容将会存储在文件中 filemode 文件打开方式,指定了filename后才能指定,默认为”a” format 日志显示格式,参数如下 datefmt 时间显示格式 level 日志级别 - format 参数说明:
%(name)s Logger的名字 %(levelno)s 数字形式的日志级别 %(levelname)s 文本形式的日志级别 %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s 调用日志输出函数的模块的文件名 %(module)s 调用日志输出函数的模块名 %(funcName)s 调用日志输出函数的函数名 %(lineno)d 调用日志输出函数的语句所在的代码行 %(created)f 当前时间,用UNIX标准的表示时间的浮点数表示 %(relativeCreated)d 输出日志信息时,自Logger创建以来的毫秒数 %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。 %(thread)d 线程ID。可能没有 %(threadName)s 线程名。可能没有 %(process)d 进程ID。可能没有 %(message)s 用户输出的消息 - 举例:
import logging logging.basicConfig(level=logging.DEBUG, # 调整日志输出等级 format="Time:'%(asctime)s' FilePath:'%(pathname)s' [line:%(lineno)d] %(levelname)s Message:'%(message)s'", datefmt='%Y-%m-%d %H:%M:%S') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') 运行结果: Time:'2019-10-03 15:09:06' FilePath:'D:/python_S26/day17/exercise.py' [line:226] DEBUG Message:'debug message' Time:'2019-10-03 15:09:06' FilePath:'D:/python_S26/day17/exercise.py' [line:227] INFO Message:'info message' Time:'2019-10-03 15:09:06' FilePath:'D:/python_S26/day17/exercise.py' [line:228] WARNING Message:'warning message' Time:'2019-10-03 15:09:06' FilePath:'D:/python_S26/day17/exercise.py' [line:229] ERROR Message:'error message' Time:'2019-10-03 15:09:06' FilePath:'D:/python_S26/day17/exercise.py' [line:230] CRITICAL Message:'critical message'
- 函数定义:
-
列举三个应用,理解日志的用法
-
logging.debug()
的应用
import logging logging.basicConfig(level=logging.DEBUG, # 这里可以通过修改输出等级来控制需要调试的时候输出 format="Time:'%(asctime)s' FilePath:'%(pathname)s' [line:%(lineno)d] %(levelname)s Message:'%(message)s'", datefmt='%Y-%m-%d %H:%M:%S') lst = list() for el in range(5): lst.append(el) logging.debug(lst) print(lst) 运行结果: [0, 1, 2, 3, 4] # 并发问题导致先输出lst Time:'2019-10-03 17:16:49' FilePath:'D:/python_S26/day17/exercise.py' [line:239] DEBUG Message:'[0]' Time:'2019-10-03 17:16:49' FilePath:'D:/python_S26/day17/exercise.py' [line:239] DEBUG Message:'[0, 1]' Time:'2019-10-03 17:16:49' FilePath:'D:/python_S26/day17/exercise.py' [line:239] DEBUG Message:'[0, 1, 2]' Time:'2019-10-03 17:16:49' FilePath:'D:/python_S26/day17/exercise.py' [line:239] DEBUG Message:'[0, 1, 2, 3]' Time:'2019-10-03 17:16:49' FilePath:'D:/python_S26/day17/exercise.py' [line:239] DEBUG Message:'[0, 1, 2, 3, 4]'
-
logging.info()
的应用
import logging logging.basicConfig(level=logging.INFO, format="Time:'%(asctime)s' FilePath:'%(pathname)s' [line:%(lineno)d] %(levelname)s Message:'%(message)s'", datefmt='%Y-%m-%d %H:%M:%S') userinfo = dict() user = input("账号:") pwd = input("密码:") userinfo[user] = pwd logging.info(f"注册的账号:{user},注册的密码:{pwd}") # 记录程序运行时的信息 运行结果: 账号:zxd 密码:zxd123 Time:'2019-10-03 17:35:07' FilePath:'D:/python_S26/day17/exercise.py' [line:251] INFO Message:'注册的账号:zxd,注册的密码:zxd123'
-
logging.error()
的应用
import logging logging.basicConfig(level=logging.INFO, format="Time:'%(asctime)s' FilePath:'%(pathname)s' [line:%(lineno)d] %(levelname)s Message:'%(message)s'", datefmt='%Y-%m-%d %H:%M:%S') try: def division(a, b): c = a / b return c division(3, 0) except ZeroDivisionError: print("除数不能为0") logging.error("输入除数为0") 运行结果: 除数不能为0 Time:'2019-10-03 17:51:46' FilePath:'D:/python_S26/day17/exercise.py' [line:266] ERROR Message:'输入除数为0'
-
-
函数式简单配置有两个弊端:
- 编码不能修改
- 中控台和文件不能同时进行(要么在中控台显示,要么写入文件)
-
logger对象配置
- 利用logger对象来操作日志文件,具体流程如下:
- 创建一个logger对象,用来操作日志
- 创建一个文件管理操作符,用来写入文件
- 创建一个屏幕管理操纵符, 用来屏幕显示
- 创建一个日志输出的格式
- 将操作符与输出格式进行绑定
- 将logger对象与操作符进行绑定
import logging logger = logging.getLogger() # 创建一个logger对象,用来操作日志 fh = logging.FileHandler("log.log", encoding="utf-8") # 创建一个文件管理操作符,用来写入文件 sh = logging.StreamHandler() # 创建一个屏幕管理操纵符, 用来屏幕显示 fmt = logging.Formatter("Time:'%(asctime)s' FilePath:'%(pathname)s' [line:%(lineno)d] %(levelname)s Message:'%(message)s'") # 创建一个日志输出的格式 fh.setFormatter(fmt) # 将文件管理操作符与输出格式进行绑定 sh.setFormatter(fmt) # 将屏幕管理操作符与输出格式进行绑定 logger.addHandler(fh) # 将logger对象与文件管理操作符进行绑定 logger.addHandler(sh) # 将logger对象与屏幕管理操作符进行绑定 logger.setLevel(logging.DEBUG) # 调整日志输出级别 logger.debug('debug message') logger.info('info message') logger.warning('warning message') logger.error('error message') logger.critical('critical message') 运行结果: Time:'2019-10-03 18:31:18,217' FilePath:'D:/python_S26/day17/exercise.py' [line:283] DEBUG Message:'debug message' Time:'2019-10-03 18:31:18,217' FilePath:'D:/python_S26/day17/exercise.py' [line:284] INFO Message:'info message' Time:'2019-10-03 18:31:18,217' FilePath:'D:/python_S26/day17/exercise.py' [line:285] WARNING Message:'warning message' Time:'2019-10-03 18:31:18,217' FilePath:'D:/python_S26/day17/exercise.py' [line:286] ERROR Message:'error message' Time:'2019-10-03 18:31:18,218' FilePath:'D:/python_S26/day17/exercise.py' [line:287] CRITICAL Message:'critical message'
- 说明:
- 通过logger对象来操作日志可以让中控台和文件同时进行
- 通过logger对象写入文件时,可以指定编码方式
- 灵活性高,创建的对象之间没有关系,通过绑定链在一起,完全可以绑定两个文件管理操作符来写入不同的文件,也可以绑定不同的输出格式
- 利用logger对象来操作日志文件,具体流程如下: