第6天,常用模块
@(python)
目录
paramiko模块
一、time模块
二、random模块
三、os 模块
四、sys模块
五、shutil模块
六、json模块与pickle模块
1. 什么是序列化?
2. 为什么要序列化?
3. 如何序列化之json和pickle
json
pickle
七、shelve模块
八、xml模块
1. 解析xml
2. 操作xml
3. 创建xml文档
方式一
方式二
方式三
九、configparser模块
1.读取
2. 改写和判断
3. 添加
4. 基于上述方法添加一个ini配置文件
十、hashlib模块
十一、subprocess模块
十二、logging模块
1. 简单将日志打印到屏幕
2. 通过logging.basicConfig函数对日志的输出格式及输出位置做相关配置
3. 如果想同时把log打印在屏幕和日志文件里,就需要了解一些复杂的知识。
四个主要类
logger
handler
Formatter
示例一
示例二
日志输出到其它位置
通过logging.config模块配置日志
paramiko模块用法:https://www.cnblogs.com/rainowl-ymj/p/7247287.html
一、time模块
在python中,有三种方式来表示时间:
- 时间戳(timestamp): 时间戳表示的是从1970年1月1日 00:00:00开始按秒计算的偏移量(1970年是unix元年)。
- 格式化的时间字符串(Format String)例如:'2017-02-15 11:40:53' 这样的时间
- 结构化的时间(struct_time): struct_time元组共有9个元素(年、月、日、时、分、秒、一年中的第几周、一年中的第几天、夏令时)
时间形式转换:
时间形式转换print(time.strftime('%Y-%m-%d %X',time.localtime()))
# 将结构化时间(struct_time)转换为格式化的时间字符串,
# time.localtime()不给时间戳默认是用当前时间戳转换成结构化时间;
# 所以要想将时间戳转换成格式化时间字符串,必须先转换成结构化时间,再将结构先时间转换成格式化时间
print(time.strptime('2017-06-05 10:50:11','%Y-%m-%d %X'))
# 将格式化的时间字符串转换为结构化时间(struct_time)
print(time.mktime(time.localtime()))
# 将结构化时间(struct_time)转换为时间戳
print(time.localtime(1496632089.0))
# 将时间戳转换成当前时区的结构化时间(struct_time)
# time.localtime()不给时间戳默认是用当前时间戳转换成结构化时间
print(time.gmtime(1496632089.0))
# 将时间戳转换成UTC时区(0时区)的结构化时间(struct_time)
转换为linux系统时间表示形式:
转换为linux系统时间print(time.asctime(time.localtime()))
# 将结构化时间(struct_time)转换为“Mon Jun 5 13:47:49 2017”这种形式的时间
print(time.ctime(1234567890))
# 将时间戳转换为“Sat Feb 14 07:31:30 2009” 这种形式的时间
其他用法:
time.sleep(1) # 休眠1秒
二、random模块
import random
print(random.random())
# float型,大于0且小于1之间的小数
print(random.randint(1,5))
# 1~5之间的整数,包含1和5
print(random.randrange(1,5))
# 1~5之间的整数,包含1,不包含5
print(random.choice([3,5,6,2,7,1]))
# 从序列中随机取出一个元素(字符串、列表、元组)
print(random.sample([6,5,7,2,8,3,1,5],3))
# 从序列中随机取出任意3个元素,元素个数可自定义
print(random.uniform(1,5))
# 取出大于1,小于5之间的任一小数,如:4.396934907979433
item = [1,2,3,4,5]
random.shuffle(item) # 打乱序列的顺序,相当于“洗牌”
print(item)
生成随机验证码:
# print(chr(65))
# print(ord('A'))
# ASCII码表 65~90 表示A-Z 97~122 表示a-z
def v_code(n):
'''生成随机验证码'''
com_char = ''
for i in range(n): # 循环n次
nums = str(random.randint(0,9))
S = chr(random.randint(65,90)) # 大写字母
s = chr(random.randint(97,122)) # 小写字母
res = random.choice([nums,s,S]) # 随机选择一个
com_char += res # 每次循环将随机选择的字符相加
return com_char
print(v_code(4))
三、os 模块
import os
# print(os.getcwd())
# 获取当前工作目录,相当Linux命令pwd
# os.chdir('E:\python\s17\day05')
# 进入指定目录,目录不存在报错,相当于Linux下的cd命令
# print(os.curdir) # 返回当前目录(“.”)
# print(os.pardir) # 返回当前目录的父目录("..")
# os.makedirs(r'E:\python\s17\day06\test\abc')
# 相当于mkdir -p,存在会报错
# os.removedirs(r'E:\python\s17\day06\test\abc')
# 若目录为空,则删除;并递归到上一级目录,如果也为空,则删除,依此类推
# os.mkdir(r'.\test')
# 在当前目录下创建一个test目录,相当于Linux命令mkdir
# os.rmdir(r'.\test')
# 删除单级空目录,若目录不为空则无法删除,报错;相当于Linux中rmdir
# os.listdir('dirname')
# 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表的形式打印
# os.remove('test') # 删除文件
# os.rename(r'.\test.txt',r'.\TEXT.txt') # 重命名,前新后旧
# print(os.stat(r'.\TEXT.txt')) # 获取文件信息
# os.linesep # 输出换行符
# os.pathsep # 输出分割文件路径的字符,win下为“;” ,Linux下为“:”
# os.name # 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
# os.system("bash command") # 运行shell命令,直接显示
# os.environ # 获取系统环境变量
# print(os.path.abspath(r'TEXT.txt')) # 获取绝对路径
# print(os.path.split(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 将所给路径分成目录和文件名(不管是否存在),并通过元组的形式返回
# print(os.path.dirname(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 提取父目录(不管是否存在)
# print(os.path.basename(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 提取最后一个文件名(不管是否存在),以'\'结尾会报错
# print(os.path.join(r'D:',r'c:\\def',r'ab\\anc\\rf',r'ac\\cd'))
# 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
# ===== 判断===========================
# print(os.path.exists(r'E:\python\s17\day06\os模块\'))
# 判断所给路径是否存在,返回True或False,以'\'结尾会报错
# print(os.path.isabs(r'E:\python\s17\day06\os模块'))
# 判断是否是绝对路径,返回True或False,以'\'结尾会报错
# print(os.path.isfile(r'TEXT.txt'))
# 判断是否是一个文件,返回True或False,以'\'结尾会报错
# print(os.path.isdir(r'E:\python\s17\day06\os模块'))
# 判断是否是一个目录,返回True或False,以'\'结尾会报错
# =======================================
# ======= 获取文件信息 =====================
# print(os.path.getatime(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 获取文件的最后访问的时间戳
# print(os.path.getmtime(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 获取文件的最后修改的时间戳
# print(os.path.getsize(r'E:\python\s17\day06\os模块\TEXT.txt'))
# 获取文件大小,单位是字节(bytes)
# ======================================
# print(os.path.normcase(r'E:/python/s17/day06/os模块/TEXT.txt'))
# 该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
# print(os.path.normpath(r'E:/python/s17/day06/../TEXT.txt'))
# 返回“E:\python\s17\TEXT.txt” ,“..”表示上级目录
# === os 路径处理 ==================
# 获取当前代码文件的所在的上层目录
# 方式一:推荐使用
import os,sys
print(__file__)
possible_topdir = os.path.normpath(
os.path.join(
os.path.abspath(__file__), # __file__表示当前文件的绝对路径
os.pardir, # 返回上一级
os.pardir # 再返回上一级
)
)
print(possible_topdir) # 会获取到day06目录的绝对路径
# sys.path.insert(0,possible_topdir) # 放入sys.path,便于调用
# 方式二:不推荐使用
print(os.path.dirname(os.path.dirname(__file__)))
四、sys模块
用于提供对python解释器相关的操作:
print(sys.argv) # 打印参数列表,第一个元素是文件本身
sys.argv[0] # 表示文件本身
sys.argv[1] # 表示命令行中命令后跟的第一个参数,以此类推。。。
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdin 输入相关
sys.stdout 输出相关
sys.stderror 错误相关
进度百分比
# 进度百分比
import sys
import time
def view_bar(num, total):
rate = float(num) / float(total)
rate_num = int(rate * 100)
r = '\r%d%%' % (rate_num, )
sys.stdout.write(r)
sys.stdout.flush()
if __name__ == '__main__':
for i in range(0, 100):
time.sleep(0.1)
view_bar(i, 100)
进度条
# 进度条
for i in range(50):
sys.stdout.write('%s\r'%("#"*i))
sys.stdout.flush()
time.sleep(0.3)
五、shutil模块
高级的文件、文件夹、压缩包处理模块
import os,shutil
# with open('test.txt','w',encoding='utf-8') as f:
# f.write('你好,世界')
# if os.path.isfile('test.txt'):
# shutil.copyfile('test.txt','test1.txt')
# 所有都是前旧后新
# shutil.copyfileobj(open('test.txt','r',encoding='utf-8'),\
# open('test2.txt','w',encoding='utf-8'))
# # 将文件内容拷贝到另一个文件中
# shutil.copyfile('test2.txt','test3.txt',follow_symlinks=True)
# 拷贝文件,目标文件无需存在,follow_symlinks=True表示保留软链接属性
# shutil.copymode('test3.txt','test5.txt')
# 仅拷贝权限,内容、组、用户均不变;目标文件必须存在
# shutil.copystat('test3.txt','test5.txt')
# 仅拷贝文件状态信息,包括:mode bits atime mtime flgs
# shutil.copy('test5.txt','test6.txt')
# 拷贝文件和权限
# shutil.copy2('test3.txt','test6.txt')
# 拷贝文件和状态信息
# import shutil
# shutil.ignore_patterns(*patterns)
# shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
#目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
# shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 拷贝时保持软链接,如不指定symlinks=True,会把软链接拷贝成硬链接
# shutil.rmtree('b')
# 递归删除目录(将目录下的文件和子目录全都删除,相当于rm -rf)
# shutil.move(r'.\a\b\b.txt',r'.\a')
# 递归移动文件,它类似mv命令,也可以对文件重命名
压缩包处理
shutil.make_archive
创建归档文件,并返回文件路径(例如zip或tar)
# 语法:
make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,dry_run=0, owner=None, group=None, logger=None)
'''
参数:
base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象
'''
# 示例
# 将当前目录下的a文件夹打包至当前目录下
res = shutil.make_archive('a','gztar',root_dir='.')
# 会生成一个a.tar.gz的文件
print(res)
# 输出:
E:\python\s17\day06\shutil模块\a.tar.gz
'''
压缩归档模式有:
gztar ---> *.tar.gz
xztar ---> *.tar.xz
bztar ---> *.tar.bz2
'''
shutil
对压缩包的处理是调用ZipFile
和TarFile
两个模块来进行的,详细:
zipfile压缩和解压缩:
import zipfile
# 压缩
z = zipfile.ZipFile('b.zip','w')
z.write(r'.\a\b')
z.close()
# 解压
x = zipfile.ZipFile('b.zip','r')
x.extractall(path='.')
x.close()
tarfile压缩解压缩:
import tarfile
# 压缩
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()
# 解压
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()
六、json模块与pickle模块
之前尝过内置函数eval()
可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads()
和eval()
;但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值。
import json
x = '[null,true,false,1]'
# print(eval(x))
# 会报错
print(json.loads(x))
# 会转换成[None, True, False, 1]
1. 什么是序列化?
我们把对象(变量)从内存中变成可存储或可传输的过程称之为序列化,在Python中叫pickling,在其他语言中被称为serialization、marshalling、flattening等等,都是一个意思。
2. 为什么要序列化?
-
持久保存状态
程序的执行就是在处理一系列状态的变化,在编程语言中,状态会以各种各样有结构的数据类型的形式被保存在内存中。但是内存是无法永久保存数据的,当程序运行一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间有结构的数据都被清空了。在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。
例如:你玩使命召唤闯到了第13关,然后你保存游戏状态,关机走人,下次再玩,还能从上次的位置开始继续闯关;还有像虚拟机的挂起等。 -
跨平台数据交互
序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好使用一种序列化格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
3. 如何序列化之json和pickle
json
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如xml,但更好的方法是序列化为json格式,因为json表示出来的就是一个字符串,可以被所有语言读取,也可以方便的存储到磁盘或者通过网络传输。json不仅是标准格式,并且比xml更快,而且可以直接在web页面中读取,非常方便。
json表示的对象就是标准的JavaScript语言的对象,json和python内置的数据类型对应如下:
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
'string' | str |
123.23 | int或float |
true/false | True/False |
null | None |
序列化代码:
import json
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic)) # <class 'dict'>
j = json.dumps(dic) # 转换为字符串形式
print(type(j)) # <class 'str'>
f = open('test2.txt','w')
f.write(j)
# 等价于json.dump(dic,f),
# json.dump(dic,f)
# `json.dump()`可以直接将字典dic以字符串的形式写入文件中
f.close()
反序列化
# 反序列化
f = open('test2.txt','r',encoding='utf-8')
data = json.loads(f.read())
# 等价于data = json.load(f),
# data = json.load(f)
#可以直接将文件内容加载并赋值
f.close()
print(type(data)) # <class 'dict'>
注意:
import json
# dct = "{'name':'caigy'}"
# j = json.loads(dct) # 这里会报错,因为json.loads()生成的数据还是{'name':'caigy'}
# 将变量dct改为下面这样
dct='{"1":"111"}'
j = json.loads(dct)
print(type(dct)) # <class 'str'>
print(type(j)) # <class 'dict'>
print(j) # 输出:{'1': '111'}
# 这样就不会报错,可以看出,要想通过json反序列化数据,那么原来的字符串数据必须是单引号里用双引号,否则就会报错。
# 无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads
pickle
pickle的用法与json一样,只是json序列化后的数据类型是str,而pickle序列化后的数据类型是bytes。
pickleimport pickle
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic)) # <class 'dict'>
p = pickle.dumps(dic)
print(type(p)) # <class 'bytes'>
print(p) # 输出b'\x80\x03}q...maleq\x05u.'
with open('data.pic','wb') as f: # 注意w是写入str,wb是写入bytes
# f.write(p) # 等价于pickle.dump(dic,f)
# 或者
pickle.dump(dic,f)
with open('data.pic','rb') as f1:
# data = pickle.loads(f1.read())
# 或者
data = pickle.load(f1)
print(data) # 输出字典{'name': 'alvin', 'sex': 'male', 'age': 23}
注:
pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的python彼此都不兼容,因此,只能用pickle保存那些不重要的数据,不能成功地反序列化也没关系。
七、shelve模块
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写,key必须为字符串,而值可以是python支持的数据类型。
利用shelve模块将数据存入文件,会产生三个分别以".bak"、".dat"、".dir" 结尾的文件
import shelve
# 存数据,存入的数据可以是列表、字典等python所支持的数据类型
f = shelve.open(r'shelve.txt')
f['stu1_info']={'name':'egon','age':18,'hobby':['piao','smoking','drinking']}
f['stu2_info']={'name':'gangdan','age':53}
f['school_info']={'website':'http://www.pypy.org','city':'beijing'}
f.close()
# 取数据,取数据时知道数据的名称,在.dir结尾的文件中可以看到,然后用列表的索引或字典的key去取值
data = shelve.open(r'shelve.txt')
print(data['stu2_info']['name'])
print(data['school_info']['city'])
data.close()
'''
# 输出结果
gangdan
beijing
'''
八、xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但是json使用起来更简单,不过在json还没诞生的时候,主要使用就是xml,至今很多传统公司,如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>
节点来区别数据结构的:
示例文件:exa.xml
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
xml协议在各个语言里都是支持的,在python中可以用以下模块操作xml;
1. 解析xml
- 利用ElementTree.XML将字符串解析成xml对象
# 利用ElementTree.XML将字符串解析成xml对象
from xml.etree import ElementTree as ET
# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()
# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)
- 利用ElementTree.parse将文件直接解析成xml对象
利用ElementTree.parse将文件直接解析成xml对象
from xml.etree import ElementTree as ET
# 直接解析xml文件
tree = ET.parse("xo.xml")
# 获取xml文件的根节点
root = tree.getroot()
2. 操作xml
# 以下得到的是迭代器,需要通过for循环查看
# print(root.iter('year')) #全文搜索
# print(root.find('country')) #在root的子节点找,只找一个
# print(root.findall('country')) #在root的子节点找,找所有
xml格式类型是节点嵌套节点,对于每一个节点均有以下功能,以便对当前节点进行操作;
'''
tag #当前节点的标签名
attrib #当前节点的属性
text #当前节点的内容
makeelement(self, tag, attrib) #创建一个新节点
append(self, subelement) #为当前节点追加一个子节点
extend(self, elements) #为当前节点扩展n个子节点
insert(self, index, subelement) #为当前节点创建子节点,然后插入指定位置
remove(self, subelement) #在当前节点中删除某个子节点
find(self, path, namespaces=None) #获取第一个寻找到的子节点
findtext(self, path, default=None, namespaces=None) #获取第一个寻找到的子节点的内容
findall(self, path, namespaces=None) #查找所有子节点
iterfind(self, path, namespaces=None) #获取所有指定的节点,并创建一个迭代器(可以被for循环)
iter(self, tag=None) #根据节点名称寻找所有指定的节点,并返回一个迭代器
itertext(self) #根据节点名称寻找所有指定的节点的内容,并返回一个迭代器
clear(self) #清空节点
get(self, key, default=None #获取当前节点的属性值
set(self, key, value) #为当前节点设置属性值
keys(self) #获取当前节点的所有属性的 key
items(self) #获取当前节点的所有属性值,每个属性都是一个键值对
'''
a. 遍历xml文件中所有内容
import xml.etree.ElementTree as ET
tree = ET.parse('exa.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根节点
print(root.tag) # 顶层标签
# 遍历XML文档的第二层
for child in root:
# 第二层节点的标签名称和标签属性
print(child.tag,child.attrib)
for i in child:
# 第二层节点的标签名称和内容
print(i.tag,i.text)
b. 遍历xml中指定的节点
import xml.etree.ElementTree as ET
tree = ET.parse('exa.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根节点
print(root.tag) # 顶层标签
# 遍历XML中所有的year节点
for node in root.iter('year'):
# 节点的标签名称和内容
print(node.tag,node.text)
c. 修改节点内容
由于修改节点时,均是在内存中进行,其不会影响文件中的内容,所以,如果想要修改,则需要重新将内存中的内容写到文件中。
# 循环所有的year节点
for node in root.iter('year'):
# 将year节点中的内容自增一
node.text = str(int(node.text)+1)
# 将year节点名改为year_up
node.tag = node.tag+'_up'
# 设备属性
node.set('updated','yes')
node.set('version','1.0')
# 删除属性
del node.attrib['updated']
# 保存至文件,可以写原文件名
tree.write('exa_3.xml',encoding='utf-8')
d. 增加子节点
# 增加节点
#在country下添加一个子节点year_new
for country in root:
#生成一个新的子节点
year_new = ET.Element('year_new')
#设置属性
year_new.set('name','alex')
#添加内容,内容为原year内容加1
year_new.text = str(int(country.find('year').text)+1)
#将新生成的子节点追加至每个country节点
country.append(year_new)
# 保存至文件中
tree.write('exa_5.xml',encoding='utf-8')
e. 删除节点
import xml.etree.ElementTree as ET
tree = ET.parse('exa_5.xml') # 直接解析xml文件
root = tree.getroot() # xml文件的根节点
for country in root:
for item in country.findall('year_new'):
# 获取country节点的year_new的内容
year = int(item.text)
if year < 2010:
# 删除指定year_new节点
print(item.tag,item.text)
country.remove(item)
# 写入文件
tree.write('exa_5.xml',encoding='utf-8')
3. 创建xml文档
方式一
#创建方式(一)
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建节点大儿子
son1 = ET.Element('son', {'name': '儿1'})
# 创建小儿子
son2 = ET.Element('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson2 = ET.Element('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son1)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式二
#创建方式(二)
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建大儿子
# son1 = ET.Element('son', {'name': '儿1'})
son1 = root.makeelement('son', {'name': '儿1'})
# 创建小儿子
# son2 = ET.Element('son', {"name": '儿2'})
son2 = root.makeelement('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
# grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
# grandson2 = ET.Element('grandson', {'name': '儿12'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son1)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式三
#创建方式(三)
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建节点大儿子
son1 = ET.SubElement(root, "son", attrib={'name': '儿1'})
# 创建小儿子
son2 = ET.SubElement(root, "son", attrib={"name": "儿2"})
# 在大儿子中创建一个孙子
grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'})
grandson1.text = '孙子'
et = ET.ElementTree(root) #生成文档对象
et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)
# xml_declaration=True 表示开启xml文档的声明
# <?xml version="1.0"?> 这样的声明
由于原生保存的XML时默认无缩进,如果想要设置缩进的话, 需要修改保存方式:
from xml.etree import ElementTree as ET
from xml.dom import minidom
def prettify(elem):
"""将节点转换成字符串,并添加缩进。
"""
rough_string = ET.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent="\t")
# 创建根节点
root = ET.Element("famliy")
# 创建大儿子
# son1 = ET.Element('son', {'name': '儿1'})
son1 = root.makeelement('son', {'name': '儿1'})
# 创建小儿子
# son2 = ET.Element('son', {"name": '儿2'})
son2 = root.makeelement('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
# grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
# grandson2 = ET.Element('grandson', {'name': '儿12'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son1)
raw_str = prettify(root)
f = open("xxxoo.xml",'w',encoding='utf-8')
f.write(raw_str)
f.close()
九、configparser模块
配置文件a.cfg内容如下:
# 注释1
; 注释2
[section1]
k1 = v1
k2:v2
user=egon
age=18
is_admin=true
salary=31
[section2]
k1 = v1
1.读取
import configparser
#读取配置文件
config = configparser.ConfigParser()
config.read('a.cfg')
#查看所有的标题
title = config.sections()
print(title) #返回['section1', 'section2']
#查看标题section1下所有key=value的key
options = config.options('section1')
print(options) #返回['k1', 'k2', 'user', 'age', 'is_admin', 'salary']
#查看标题section2下所有key=value的(key,value)元组格式
tu_title = config.items('section2')
print(tu_title) #返回[('k1', 'v1')],列表中的元素是元组
#查看标题section1下user的值=>字符串格式
val = config.get('section1','user')
print(val) #返回egon
#查看标题section1下age的值=>整数格式
age = config.getint('section1','age')
print(age) #返回18
#查看标题section1下is_admin的值=>布尔值格式
val_1 = config.getboolean('section1','is_admin')
print(val_1) #返回True
#查看标题section1下salary的值=>浮点型格式
val_2 = config.getfloat('section1','salary')
print(val_2) #返回31.0
2. 改写和判断
#改写
config = configparser.ConfigParser()
config.read('a.cfg')
#删除整个标题section2
config.remove_section('section2')
#删除标题section1下的k1
config.remove_option('section1','k1')
#判断是否存在某个标题
print(config.has_section('section1')) #存在返回True
#判断标题section1下是否有user
print(config.has_option('section1','user')) #存在返回True
#最后将修改的内容写入文件,完成最终的修改
config.write(open('a.cfg','w'))
3. 添加
#添加一个标题
config.add_section('egon')
#在标题egon下添加name=egon,age=18的配置
config.set('egon','name','egon')
config.set('egon','age','18') #所有内容必须是字符串
# 添加新节点
config = configparser.ConfigParser()
config.add_section('caigy')
config['caigy']['username'] = 'caigy'
config['caigy']['age'] = '18'
config.write(open('test.ini','w',encoding='utf-8'))
'''
生成的内容如下:
[caigy]
username = caigy
age = 18
'''
4. 基于上述方法添加一个ini配置文件
# 基于上述方法添加一个ini文档
import configparser
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
'Compression': 'yes',
'CompressionLevel': '9'}
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022' # mutates the parser
topsecret['ForwardX11'] = 'no' # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
config.write(configfile)
以上内容实现的ini配置文件内容如下:
[DEFAULT]
serveraliveinterval = 45
compression = yes
compressionlevel = 9
forwardx11 = yes
[bitbucket.org]
user = hg
[topsecret.server.com]
host port = 50022
forwardx11 = no
十、hashlib模块
hash:一种算法 ,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法。
三个特点:
- 1.内容相同则hash运算结果相同,内容稍微改变则hash值则变
- 2.不可逆推
- 3.相同算法:无论校验多长的数据,得到的哈希值长度固定。
import hashlib
m = hashlib.md5() # m = hashlib.sha256()
m.update('hello'.encode('utf-8')) #哈希前必须先encode
print(m.hexdigest()) #输出16进制的md5校验码
print(m.digest()) #2进制格式hash
'''
注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样
但是update多次为校验大文件提供了可能。
'''
import hashlib
# ######## md5 ########
hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())
# ######## sha1 ########
hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())
# ######## sha256 ########
hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())
# ######## sha384 ########
hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())
# ######## sha512 ########
hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())
对文件进行哈希校验
import hashlib
m = hashlib.md5()
# 由于哈希前必须encode,所以可以直接用rb的方式打开文件
# 这样在哈希时就不用再encode了
with open(r'E:\python\s17\day01\login.py','rb') as f:
for line in f:
m.update(line) #每次update一行,最终得到的结果永远一样
print(m.hexdigest())
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。
模拟撞库破解密码:
import hashlib
passwds=[
'alex3714',
'alex1313',
'alex94139413',
'alex123456',
'123456alex',
'a123lex',
]
def make_passwd_dic(passwds):
dic={}
for passwd in passwds:
m=hashlib.md5()
m.update(passwd.encode('utf-8'))
dic[passwd]=m.hexdigest()
return dic
def break_code(cryptograph,passwd_dic):
for k,v in passwd_dic.items():
if v == cryptograph:
print('密码是===>\033[46m%s\033[0m' %k)
cryptograph='aee949757a2e698417463d47acac93df'
break_code(cryptograph,make_passwd_dic(passwds))
为了解决这个缺陷,python 还有一个 hmac 模块,它内部对我们创建 key 和 内容 再进行处理然后再加密
散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪;
一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
import hmac
h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))
h.update(bytes('admin',encoding="utf-8"))
print(h.hexdigest())
十一、subprocess模块
subprocess模块允许您生成新进程,连接到输入/输出/错误管道,并获取其返回代码。 该模块意图替换几个较旧的模块和功能:
os.system
os.spawn*
常用subprocess方法示例
#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])
#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0
#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')
#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'
#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'
上面那些方法,底层都是封装的subprocess.Popen
poll() #检查子进程是否终止。 返回returncode
wait() #等待子进程终止。 返回returncode属性。
terminate() #杀掉所启动进程
communicate() #等待任务结束
stdin #标准输入
stdout #标准输出
stderr #标准错误
pid #子进程的进程ID。
#例子
>>> p = subprocess.Popen("df -h|grep disk",stdout=subprocess.PIPE,shell=True)
>>> p.stdout.read() #输出的是bytes类型,如果输出的有汉字则会出现乱码,因此要进行decode('utf-8')
b'/dev/disk1 465Gi 64Gi 400Gi 14% 16901472 104938142 14% /\n'
#stdout=subprocess.PIPE #表示将命令执行的正确输出,输出到管道里;
#shell=True #这个参数会传递给Popen 代表的是执行命令之前先开一个shell来执行 ,默认的shell为/bin/sh ,Popen(['ls', '-l'], shell=True) 等效于 Popen(['/bin/sh',' ls', '-l']),而如果你不加shell=True 则会开启一个子进程 并且在子进程直接执行。
subprocess.run()
>>> subprocess.run(["ls", "-l"]) # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
-rw-r--r--. 1 root root 2283 May 22 10:12 SQL.txt
drwxr-xr-x. 2 root root 4096 May 13 17:31 Templates
drwxr-xr-x. 2 root root 4096 May 13 17:31 Videos
-rw-r--r--. 1 root root 10143 Mar 13 18:01 zabbix笔记.txt
CompletedProcess(args=['ls', '-l'], returncode=0)
>>> subprocess.run("exit 1",shell=True,check=False)
CompletedProcess(args='exit 1', returncode=1)
>>> subprocess.run("exit 1",shell=True,check=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/rh/rh-python35/root/usr/lib64/python3.5/subprocess.py", line 711, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
#check=True #表示开启检测命令执行状态,如果返回状态码为非0测报错。
模拟ls /root | grep "txt$"
的功能
res_1 = subprocess.Popen('ls /root',shell=True,
stdout=subprocess.PIPE, #标准正确输出放入管道
stderr=subprocess.PIPE) #错误输出放入另一管道
res_2 = subprocess.Popen('grep txt$',shell=True,
stdin=res_1.stdout, #标准输入接收res_1的标准正确输出
stdout=subprocess.PIPE) #标准输出放入管道
print(res_2.stdout.read().decode('utf-8')) #结果是bytes类型,需要decode
#输出:
SQL.txt
zabbix笔记.txt
十二、logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug(), info(), warning(), error() and critical() 5个级别,下面我们看一下怎么用。
1.简单将日志打印到屏幕
import logging
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
'''
屏幕上打印:
WARNING:root:This is warning message
'''
默认情况下,logging将日志打印到屏幕,日志级别为WARNING;
日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别
日志级别
Level | Numeric | When it's used |
---|---|---|
DEBUG | 10 | 详细信息,通常仅在调试程序时才用 |
INFO | 20 | 确认程序按预期执行 |
WARNING | 30 | 表明意外发生的事情,或在不久的将来表示某些问题(例如“磁盘空间低”),该软件仍然按预期工作。 |
ERROR | 40 | 由于更严重的问题,软件无法执行某些功能。 |
CRITICAL | 50 | 一个严重的错误,表示程序本身可能无法继续运行。 |
2. 通过logging.basicConfig函数对日志的输出格式及输出位置做相关配置
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='myapp.log',
filemode='w')
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
./myapp.log文件中内容为:
Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message
format日志格式
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 | 用户输出的消息 |
3. 如果想同时把log打印在屏幕和日志文件里,就需要了解一些复杂的知识。
python使用logging
模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:
- logger 提供了应用程序可以直接使用的接口;
- handler 将(logger创建的)日志记录发送到你定义的输出位置,可以是屏幕、文件、远程网络(比如通邮件发送)
- filter 提供了过滤功能,可以过滤包含什么字段的怎么去发送日志,这个比较复杂,用得也比较少。
- formatter 设置日志记录的最终输出格式。
四个主要类
logger
- 每个程序在输出日志信息之前都要获得一个
logger
。logger
通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的logger
:
logger = logging.getLogger("chat.gui")
核心模块可以这样:
logger = logging.getLogger("chat.kernel")
- 指定最低的日志级别,低于设定的最低日志级别的日志将被忽略。debug是最低的内置级别,critical为最高。
logger.setLevel(logging.DEBUG) #最低日志级别为DEBUG
- 创建一个日志格式器,规定了日志输出格式:
fmter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger的其他操作:
logger.addFilter(filt) #添加指定的filter
logger.removeFilter(filt) #删除指定的filter
logger.addHandler(hdlr) #增加指定的handler
logger.removeHandler(hdlr) #删除指定的handler
logger.debug(msg)、logger.info(msg)、logger.warning(msg)、logger.error(msg)、logger.critical(msg) #生成相应级别的日志信息
handler
handler对象负责发送相关的信息到指定目的地。python的日志系统有多种Handler可以使用。
- logging.StreamHandler() 可以向屏幕输出日志信息
-
- logging.FileHandler(filename[,mode]) 用于向一个文件输出日志信息。
- filename 是文件名,必须指定一个文件名;
- mode是文件的打开方式,参见python内置函数open()的用法。默认打开方式是'a',即追加到文件末尾。
-
- handlers.RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]]) 用于切割日志文件,当日志文件达到指定的大小之后,自动把文件改名,然后创建一个新的同名日志文件继续输出。比如日志文件是chat.log,当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。
- filename和mode两个参数和FileHandler一样;
- maxBytes 用于指定日志文件的最大文件大小,单位是Bytes(字节);如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名的过程就不会发生。
- backupCount 用于指定保留的备份文件的个数;比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
-
- handlers.TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]]) 与RotatingFileHandler类似,也是用于切割日志文件,不过,它是通过间隔一定时间来自动创建新的日志文件;重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。
- 其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义;
- interval 是指时间间隔;
-
when 是一个字符串,用来表示时间间隔的单位,不区分大小写。它有以下取值:
- S 秒
- M 分
- H 小时
- D 天
- W 每星期(interval==0时代表星期一)
- midnight 每天凌晨
注:想要使用RotatingFileHandler和TimedRotatingFileHandler ,需要导入handlers模块,如下:
from logging import handlers
#用法
fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)
handler相关的其它操作
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG) #指定被处理的信息级别
ch.setFormatter(fmter) #给这个handler选择一个日志输出格式,fmter是通过logging.Formatter()定义好的;
ch.addFilter(filter)、ch.removeFilter(filter) #新增或删除一上filter对象
Formatter
logging.Formatter()的用法在logger的第3条有所体现
示例一
需求:将日志信息同时输出到屏幕和文件中,设定输出到屏幕上的日志级别为DEBUG,设定输出到文件中的日志级别为WARNING。
import logging
#1 创建Logger,设定最低日志级别
logger = logging.getLogger('Chat.Gui')
logger.setLevel(logging.DEBUG) #最低日志级别为DEBUG
#2 创建屏幕输出,和日志级别DEBUG
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
#3 创建文件输出,和日志级别为WARNING
fh = logging.FileHandler('access.log',encoding='utf-8')
fh.setLevel(logging.WARNING)
#4 创建一个日志格式器
fmter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s : %(message)s')
#5 添加日志格式器到 ch 和 fh
ch.setFormatter(fmter)
fh.setFormatter(fmter)
#6 添加ch 和 fh至logger
logger.addHandler(ch)
logger.addHandler(fh)
#7 分别输出各个日志级别的信息进行测试
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
代码执行后,输出到屏幕的日志级别是DEBUG,所以记录了DEBUG级别以上的所有日志信息,如下图:
Paste_Image.png输出到日志文件的日志级别是WARNING,所以只记录WARNING以上级别的日志信息,如下图:
Paste_Image.png示例二
自动分割日志文件
通过规定文件大小分割或者通过规定时间间隔分割
import logging
from logging import handlers
import time
#1 创建logger
logger = logging.getLogger(__name__)
log_file = 'timelog0.log'
#设定日志级别
logger.setLevel(logging.WARNING)
#生成日志分割处理器
#按日志大小分割
# fh = handlers.RotatingFileHandler(filename=log_file,
# encoding='utf-8',
# maxBytes=10,
# backupCount=3)
#按时间间隔分割
fh = handlers.TimedRotatingFileHandler(filename=log_file,
encoding='utf-8',
when='S',
interval=5,
backupCount=3)
#生成日志格式器
fmtter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')
#为fh添加fmtter
fh.setFormatter(fmtter)
#添加处理器
logger.addHandler(fh)
#测试
logger.warning('test message 1')
time.sleep(2)
logger.warning('test message 2')
time.sleep(2)
logger.warning('test message 3')
time.sleep(2)
logger.warning('test message 4')
time.sleep(2)
logger.warning('test message 5')
日志输出到其它位置
logging.handlers.SocketHandler: 远程输出日志到TCP/IP sockets
logging.handlers.DatagramHandler: 远程输出日志到UDP sockets
logging.handlers.SMTPHandler: 远程输出日志到邮件地址
logging.handlers.SysLogHandler: 日志输出到syslog
logging.handlers.NTEventLogHandler: 远程输出日志到Windows NT/2000/XP的事件日志
logging.handlers.MemoryHandler: 日志输出到内存中的制定buffer
logging.handlers.HTTPHandler: 通过"GET"或"POST"远程输出到HTTP服务器
由于StreamHandler和FileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中。
上述其它处理方式的使用请参见python3.5官方手册
通过logging.config模块配置日志
#logger.conf
###############################################
[loggers]
keys=root,example01,example02
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
###############################################
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('myapp.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form02
args=('myapp.log', 'a', 10*1024*1024, 5)
###############################################
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example01")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example02")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')