模块和包
什么是模块?
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。
模块即一个py文件
模块分为三种
1.python标准库
2.第三方模块
3.自定义模块
另外,使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。
定义规范
1、定义模块名称不能以数字开头
2.不能和默认模块重名
定义格式
开头:
介绍模块的作用
里面包含的功能
作者的联系方式等等
模块定义
"""
变量
输出*
"""
star = "**********"
"我的作用是付了10个星号"
def p_stat():
"我的作用是返回20个星号"
return "*"*20
模块导入
1. import 模块名1 [as 别名1], 模块名2 [as 别名2],…:使用这种语法格式的 import 语句,会导入指定模块。不仅如此,当需要使用模块中的成员时,需用该模块名(或别名)作为前缀,否则 Python 解释器会报错。
2. from 模块名 import 成员名1 [as 别名1],成员名2 [as 别名2],…: 使用这种语法格式的 import 语句,只会导入模块中指定的成员,而不是全部成员。同时,当程序中使用该成员时,无需附加任何前缀,直接使用成员名(或别名)即可。
注意:
1、用 [] 括起来的部分,可以使用,也可以省略。
2、其中,第二种 import 语句也可以导入指定模块中的所有成员,即使用 form 模块名 import *,但此方式不推荐使用,
__all__可以进行限制
__name__
Make a .py both importable and executable
如果我们是直接执行某个.py文件的时候,该文件中那么__name__ == "main"是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时name的值就是我们这个py文件的名字而不是main。
这个功能还有一个用处:调试代码的时候,在”if name == 'main'“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!
示例 打印出20个星号
创建一个功能模块,该模块可以被调用import
在模块中包含一个功能函数,函数的作用返回20个hello
import printstar
"""
#print(dir(printstar)) 列出属性
# print(printstar.p_star()) 打印出20个星号
#print(printstar.star) 打印出10个星星
"""
star20 = printstar.p_star()
print(star20)
print("hello")
print((printstar.star)*2)
包
包:一个包就是一个文件夹(一个包含__init__.py的文件夹)
导包秘密
sys.path
1.解释器按照sys.path的路径模块与包的导入
2.解释器执行一个程序会将启动程序文件的目录添加到sys.path
导包路径
绝对路径导包: os.listdir(os.getcwd()) 使用os模块获取当前路径,插入到sys.path路径列表里面去
相对路径导包:. 当前路径 ..上层路径 不建议使用
导包三种方式
1 from 报名 import 模块名
2 from 包1.包2.包3 import 模块名
支持:from 包1.包2.包3.模块名 import 成员变量
支持:from 包1.包2.包3 import 模块名
不支持:from 包1.包2 import 包3.成员变量
3 import 包
不支持 包.模块。成员变量
subprocess模块
subprocess是python 2.4中新增的模块,它允许你生成新的进程,连接到他们的input/output/erro管道,并获取他们的返回码。这个模块的目的在于替换几个旧的模块和方法。
- os.system
- os.spawn*
subprocess模块中的常用函数
函数 | 描述 |
---|---|
subprocess.run() | python 3.5中新增的函数,执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。 |
subprocess.call() | 执行指定的命令,返回命令执行 |
subprocess.check_call() | python 2.5中新增的函数。执行指定的命令,如果执行成功则返回状态码,否则抛出异常,其功能等价于subprocess.run(...,ckeck=True) |
subprocess.check_output | python2.7中新增的函数,执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。 |
subprocess.getoutput(cmd) | 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd) |
subprocess.getstatusoutput(cmd) | 执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。 |
说明:
- 1.在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
- 2.在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
- 3.subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
- 4.subprocess.getoutput()和subprocess.getstatusoutput()函数是来自Python 2.x的commands模块的两个遗留函数。它们隐式的调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。另外,它们从Python 3.3.4开始才支持Windows平台。
2. 上面各函数的定义及参数说明
函数参数列表:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
subprocess.getstatusoutput(cmd)
subprocess.getoutput(cmd)
参数说明:
-
args: 要执行的shell命令,默认应该是一个字符串序列,如['df', '-Th']或('df', '-Th'),也可以是一个字符串,如'df -Th',但是此时需要把shell参数的值置为True。
-
shell: 如果shell为True,那么指定的命令将通过shell执行。如果我们需要访问某些shell的特性,如管道、文件名通配符、环境变量扩展功能,这将是非常有用的。当然,python本身也提供了许多类似shell的特性的实现,如glob、fnmatch、os.walk()、os.path.expandvars()、os.expanduser()和shutil等。
-
check: 如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常,且该异常对象会包含 参数、退出状态码、以及stdout和stderr(如果它们有被捕获的话)。
-
stdout, stderr:input: 该参数是传递给Popen.communicate(),通常该参数的值必须是一个字节序列,如果universal_newlines=True,则其值应该是一个字符串。
run()函数默认不会捕获命令执行结果的正常输出和错误输出,如果我们向获取这些内容需要传递subprocess.PIPE,然后可以通过返回的CompletedProcess类实例的stdout和stderr属性或捕获相应的内容;
call()和check_call()函数返回的是命令执行的状态码,而不是CompletedProcess类实例,所以对于它们而言,stdout和stderr不适合赋值为subprocess.PIPE;
check_output()函数默认就会返回命令执行结果,所以不用设置stdout的值,如果我们希望在结果中捕获错误信息,可以执行stderr=subprocess.STDOUT。
- universal_newlines: 该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时stdout和stderr的输出是字节序列;当该参数的值设置为True时,stdout和stderr的输出是字符串。
struct
用处
1.按照指定格式将python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时现将int转换为字节流,然后再发送。
2.按照指定格式将字节流转换为python指定的数据类型。
3.处理二进制数据,如果用struct来处理文件的话,需要用“wb”,"rb"以二进制(字节流)写读的方式来处理文件;
4.处理C语言中的结构体
struct模块中的函数
函数 | return | explain |
---|---|---|
pack(fmt,v1,v2...) | string | 按照给定的格式(fmt),把数据转化成字符串(字节流),并将该字符串返回 |
pack_into(fmt,buffer,offset,v1,v2…) | None | 按照给定的格式(fmt),将数据转换层字符串(字节流)并将字节流写入以offset开始的buffer中。(buffer为可写的缓冲区,可用array模块) |
unpack(fmt,v1,v2…..) | tuple | 按照给定的格式(fmt)解析字节流,并返回解析结果 |
pack_from(fmt,buffer,offset) | tuple | 按照给定的格式(fmt)解析以offset开始的缓冲区,并返回解析结果 |
calcsize(fmt) | size of fmt | 计算给定的格式(fmt)占用多少字节的内存,注意对齐方式 |
格式化字符串
当打包或者解包时,需要按照特定的方式来打包或者解包,该方法就是格式化字符串,他指定了数据类型,除此之外,还有用于控制字节流顺序,大小和对齐方式的特殊字符。
对齐方式
为了同C中的结构体交换数据,还要考虑c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换,可以用格式中的第一个字符来改变对齐方法
Character | Byte order | Size | Alignment |
---|---|---|---|
@(默认) | 本机 | 本机 | 本机,凑够4字节 |
= | 本机 | 标准 | none,按原字节数 |
< | 小端 | 标准 | none,按原字节数 |
> | 大端 | 标准 | none,按原字节数 |
! | network(大端) | 标准 | none,按原字节数 |
格式符
格式符 | C语言类型 | Python类型 | Standard size |
---|---|---|---|
x | pad byte(填充字节) | no value | |
c | char | string of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I(大写的i) | unsigned int | integer | 4 |
l(小写的L) | long | integer 4 | |
L | unsigned long | long 4 | |
q | long long | long | 8 |
Q | unsigned long long | long 8 | |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | string | |
p | char[] | string | |
P | void * | long |
注
- _Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;
- q和Q只适用于64位机器;
- 每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;
- P用来转换一个指针,其长度和计算机相关;
- f和d的长度和计算机相关;
import struct
data = struct.pack("i",234) #转换为固定字节流
print("data:",data)
print(struct.unpack("i",data)) #将字节流转换为元组
结果:
data: b'\xea\x00\x00\x00'
(234,)
sys
sys模块是与解释器交互的一个模块
sys.path 当前sys中的路径
sys.argv 命令行参数list,第一个元素是程序本身路径
sys.exit() 退出程序,正常退出是exit()
sys.version #获取python解释器程序的版本信息
sys.maxint #最大的int值
sys.path #返回模块的搜索路径,初始化pythonpath的环境变量的值
sys.platform #返回操作系统平台名称
OS模块
一个用于处理文件和目录的模块
os模块是与操作系统交互的一个接口
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ('.')
os.pardir 获取当前目录的父目录字符串名:('..')
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.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") 运行shell命令,直接显示
os.environ 获取系统环境变量
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(path) 返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
1.当前路径及路径下的文件
os.getcwd():查看当前所在路径。
os.listdir(path):列举目录下的所有文件。返回的是列表类型。
import os
print(os.getcwd()) #查看当前所在路径
print(os.listdir(os.getcwd())) #列举目录下所有的文件,返回的是列表类型
file 当前文件
os.path.abspath(file) 当前文件路径
os.path.dirname(os.path.abspath(file))当前文件的文件夹路径
hashlib模块
算法介绍
python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
是不可以回溯的。只能加密不能解密
MD5是最常见的摘要算法,速度很快,生成结果是固定的16字节,通常用一个32位的16进制字符串表示。SHA1算法更安全点,它的结果是20字节长度,通常用一个40位的16进制字符串表示。而比SHA1更安全的算法是SHA256和SHA512等等,不过越安全的算法越慢,并且摘要长度更长。
单个加密
import hashlib #调用模块
hd = hashlib.md5() #定义变量
s1 = "li".encode() #字符串内容编码
hd.update(s1) #加密
print(hd.hexdigest()) #输出加密字符,
结果:d70c1e5d44de8a9150eb91ecff563578
加密多个结果
import hashlib #调用模块
hd = hashlib.md5() #定义变量
s1 = "li".encode() #字符串内容编码
s2 = "ww".encode()
hd.update(s2) #加密
hd.update(s1)
print(hd.hexdigest()) #输出加密字符 b71634e4bf21207a98247028775cd4ab
等同于
import hashlib #调用模块
hd = hashlib.md5() #定义变量
s3 = "liww".encode()
hd.update(s3)
print(hd.hexdigest()) #b71634e4bf21207a98247028775cd4ab
多个加密内容:hexdigest会将两个加密内容组合到一起,进行加密
MD5()加默认值
import hashlib
md = hashlib.md5("qwe".encode())
pwd = "123".encode()
md.update(pwd)
print(md.hexdigest()) #200820e3227815ed1756a6b531e7e0d2
等同于
import hashlib
md = hashlib.md5()
# print("qwe".encode())
pwd = "123".encode()
md.update("qwe123".encode())
print(md.hexdigest()) #200820e3227815ed1756a6b531e7e0d2
在update时,md5的值会放在update内容前面
__init__文件
init.py该文件的作用就是相当于把自身整个文件夹当作一个包来管理,每当有外部导入的时候会自动执行里面的代码。
导入包的时候,默认会执行__init文件
主要功能
1. 标识该目录是一个python的模块包(module package)
如果你是使用python的相关IDE来进行开发,那么如果目录中存在该文件,该目录就会被识别为 module package 。
2. 简化模块导入操作
3.控制模块导入
4.偷懒的导入方法
__all__ 关联了一个模块列表,当执行 from xx import * 时,就会导入列表中的模块。
5. 配置模块的初始化操作
在了解了__init__.py的工作原理后,应该能理解该文件就是一个正常的python代码文件。
因此可以将初始化代码放入该文件中。
__all__ = [""]
作用: 在import导包时候 使用*导入成员变量时,可以使用__all__ 限制导入包的成员斌量。
使用*时,会导入__all__列表里面的成员变量
示例
//定义两个文件夹,在protao文件夹中定义day5包,里面创建函数,并在__init__中打印输出内容。
//在pro文件夹定义main包,里面去调用day5中定义的函数
定义内容
protao文件夹内容
//__init__
print("__init__")
//day5
__all__ = ["x","day5"]
x = 10
y = 20
def day5():
print("5day")
pro文件夹内容
//main
import os
import sys
ww = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #取出上层文件夹路径
sys.path.append(ww) #将上层文件夹路径加入到系统变量中
from protao.day5 import * #调用protao.day5中的所有成员元素
day5()
print(x)
print(y)
#因为y没有在__all__中定义,所以在导入所有包的时候是找不到的y这个变量的,x和day5在__all__中定义了,所以是可以找到的的
re模块
就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
功能:针对字符串实现模糊匹配
正则符号
//元字符
. :通配符:除换行符以外的任意一个符号
^ :以..开头
$ :以..结尾
//重复符号
* :前一个字符重复0~n次
+ :前一个字符重复1~n次
? :前一个字符重复0~1次
{}:任意指定次数
{n,}:前一字符最少出现n次
{,n}:钱一个字符最多出现0~n次
() :表示一个整体
[] :匹配[]中指定范围内的任意一个字符
[abc]相当于是一个符号(每次匹配一个字符)找出包含a或b或c
\ :转移符号
\d:匹配任何十进制数,相当于[0-9]
\D:匹配任何非数字字符;相当于[^0-9]
\s:空表字符:空格 \t\r\n\f\v
\S:非空白字符
\w:单词字符:[A-Za-z0-9~]
\W:非单词字符
\b:匹配一个特殊字符,比如空格 , & #等
- :表示一个范围
1-9:表示1-9的所有数字
| :或者
字符集
[^] :取反,非
ret = re.findall("c[^0-9]+e","coe,c12e,cwe,c.e c8e")
print(ret) #['coe', 'cwe,c.e']
贪婪匹配
满足匹配时,在满足匹配时,匹配尽可能的长的字符串,在匹配时默认贪婪匹配
ret = re.findall("a.*c","abcabcabu")
print(ret) #['abcabc']
//取消贪婪匹配
ret = re.findall("a.*?c","abcabcabu")
print(ret) #['abc', 'abc']
取消贪婪匹配 ?
.findall
将匹配的所有的内容放在一个列表里面
re.findall("规则","待匹配字符串")
.search()
函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
re.search("规则","待匹配字符串")
ret = 'www.baidu.com,www.oldboy.com,www.jd.com'
ret2 = re.search("www.(baidu|oldboy|jd).com",ret)
print(ret2) #<_sre.SRE_Match object; span=(0, 13), match='www.baidu.com'>
print(ret2.group()) #www.baidu.com #元素值
print(ret2.span()) #(0, 13) #索引位置
.match()
同search,不过尽在字符串开始处进行匹配
ret = 'www.baidu.com,www.oldboy.com,www.jd.com'
ret2 = re.match("www.(baidu|oldboy|jd).com",ret)
print(ret2) #<_sre.SRE_Match object; span=(0, 13), match='www.baidu.com'>
print(ret2.group()) #www.baidu.com
print(ret2.span()) #(0, 13)
.split()
分割
ret = "hello,world alvin|yuan"
print(ret.split(",")) #['hello', 'world alvin|yuan']
print(re.split("[, |]",ret)) #['hello', 'world', 'alvin', 'yuan']
compile()
编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。)
obj = re.compile("\d{3}")
ret = obj.search('abc123wwww')
print(ret) #<_sre.SRE_Match object; span=(3, 6), match='123'>
print(ret.group()) #123
ret = obj.search("12sdf34sd446xcfg433")
print(ret) #<_sre.SRE_Match object; span=(9, 12), match='446'>
print(ret.group()) #446
示例
完全匹配
whl = "welome to Python Wolrd! Python is nice!"
ret = re.findall("Python",whl)
print(ret) #['Python', 'Python']
. 点
ret = "room zoom home yoom"
print(re.findall(".oo.",ret)) #['room', 'zoom', 'yoom']
print(re.findall("h..e",ret)) #['home']
^ $
whl = "welome to Python Wolrd! Python is nice!"
print(re.findall("^to",whl)) #[]
print(re.findall("^w",whl)) #['w']
print(re.findall("^we",whl)) #['we']
print(re.findall("Wolrd$",whl)) #[]
print(re.findall("ce!",whl)) #['ce!']
print(re.findall("^weomenice$",whl)) #[]
print(re.findall("^welome.*nice!$",whl)) #['welome to Python Wolrd! Python is nice!']
* + ? {}
ret = "come commmme hi coe yuan comme hello world commmmmme"
print(re.findall("com*e",ret)) #['come', 'commmme', 'coe', 'comme', 'commmmmme']
print(re.findall("com+e",ret)) #['come', 'commmme', 'comme', 'commmmmme']
print(re.findall("com?e",ret)) #['come', 'coe']
print(re.findall("com{4}e",ret)) #['commmme']
print(re.findall("com{,4}e",ret)) #['come', 'commmme', 'coe', 'comme']
print(re.findall("com{4,}e",ret)) #['commmme', 'commmmmme']
[] ()
ret = "abd acd abcd a,d ad"
print(re.findall("a[bc]d",ret)) #['abd', 'acd']
print(re.findall("a[b,c]d",ret)) #['abd', 'acd', 'a,d']
num = "12,45,67,89,0,yuan,Alvin,100"
print(re.findall("[1-8]+",num)) #['12', '45', '67', '8', '1']
print(re.findall("\w+",num)) #['12', '45', '67', '89', '0', 'yuan', 'Alvin', '100']
print(re.findall('www.(baidu|jd|tb).com',"'www.baidu.com,www.tb.com,www.jd.com'")) #['baidu', 'tb', 'jd']
# //取消优先获取
print(re.findall('www.(?:baidu|jd|tb).com',"'www.baidu.com,www.tb.com,www.jd.com'")) #['www.baidu.com', 'www.tb.com', 'www.jd.com']
json模块
json就是序列化和反序列化的过程
join序列化:将本语言支持的数据对象转换为join字符串的过程
join反序列化:将join字符串转换为本语言支持的数据对象的过程
语法
//序列化
json.dumps(n)
//反序列化
json.loads()
原文
s = "hi,li"
b = True
i = 100
l = ['s','b','i']
d = {'name':"li","age":18,"hobby":None}
序列化方法
转换为json字符串
print(json.dumps(s)) #"hi,li"
print(json.dumps(b)) #true
print(json.dumps(i)) #100
print(json.dumps(l)) #["s", "b", "i"]
print(json.dumps(d)) #{"name": "li", "age": 18, "hobby": null}
将json字符串存储到文件中
f = open("json.txt","a")
f.write(json.dumps(s)+"\n")
f.write(json.dumps(i)+"\n")
f.write(json.dumps(l)+"\n")
f.write(json.dumps(d)+"\n")
f.close()
反序列化
将json字符串转换为python文件
反序列化一行
with open("json.txt","r") as f:
json_str = f.read()
print(json_str)
#反序列化 loads
date = json.loads(json_str)
print(date)
print(type(date))
反序列化多行
with open("json.txt","r") as f:
for i in f:
print(i,end="")
date = json.loads(i)
print(date)
print(type(date))
logging日志模块
日志是一种可以追溯某些软件运行时所发生的的事件的方法,
日志级别
级别 | 使用场景 |
---|---|
DEBUG | 详细信息,典型的调试问题时会感兴趣 |
INFO | 证明事情按预期工作,关键时间 |
WARNING | 表名发生一些意外,或者不久的将来发生的问题。软件还在正常工作 |
ERROR | 由于更严重的问题,软件已不能执行一些功能了,一般错误信息 |
CRITICAL | 严重错误,表明软件已不能继续运行了 |
NOTICE | 不是错误,但是需要处理,普通但是重要的事件 |
ALERT | 需要立即修复,例如系统数据库损坏 |
EMERGENCY | 紧急情况,系统不可用,一般会通知所有用户 |
一条日志信息对应的是一个事件的发生,而一个事件通常需要包括以下几个内容:
- 事件发生时间
- 事件发生位置
- 事件的严重程度--日志级别
- 事件内容
上面这些都是一条日志记录中可能包含的字段信息,当然还可以包括一些其他信息,如进程ID、进程名称、线程ID、线程名称等。日志格式就是用来定义一条日志记录中包含那些字段的,且日志格式通常都是可以自定义的。
debug < info < warning < ERROR < CRITICAL
logging模块的日志级别:
logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱。
日志等级(level) | 描述 |
---|---|
DEBUG | 最详细的日志信息,典型应用场景是 问题诊断 |
INFO | 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作 |
WARNING | 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的 |
ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息 |
开发应用程序或部署开发环境时,可以使用DEBUG或INFO级别的日志获取尽可能详细的日志信息来进行开发或部署调试;
应用上线或部署生产环境时,应该使用WARNING或ERROR或CRITICAL级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。日志级别的指定通常都是在应用程序的配置文件中进行指定的。
logging默认warning级别,只有大于等于warning级别的日志才会显示
日志是默认以追加的方式写入到文件中的
导入模块
import logging
获取日志有两种方法
1.日志模块1.
logging.basicConfig
2.日志模块2 日志对象
logging.getLogger
logging.basicConfig模块
设置日志
import logging
LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
DATE_FORMAT = '%Y-%m-%d %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
logging.basicConfig(level=logging.DEBUG,
format=LOG_FORMAT,
datefmt = DATE_FORMAT ,
filename=r"d:\test\test.log" #有了filename参数就不会直接输出显示到控制台,而是直接写入文件
)
logging.debug("msg1")
logging.info("msg2")
logging.warning("msg3")
logging.error("msg4")
logging.critical("msg5")
或
//修改logging默认级别为DEBUG
logging.basicConfig(level=logging.DEBUG, #配置默认日志界别
filename="logs.log", #配置日志输出路径
format="%(asctime)s:%(name)s:%(message)s %(levelname)s,行数:%(lineno)s", #定义日志输出格式
datefmt="%Y%m%d %H:%M:%S" #添加事件输出
)
//使用logger对象进行日志记录
logging.debug("DEBUG信息")
logging.info("info信息")
logging.warning("warning信息")
logging.error("error信息")
logging.critical("critical信息")
logging.basicConfig()函数包含参数说明
参数名称 | 描述 |
---|---|
filename | 指定日志输出目标文件的文件名(可以写文件名也可以写文件的完整的绝对路径,写文件名日志放执行文件目录下,写完整路径按照完整路径生成日志文件),指定该设置项后日志信心就不会被输出到控制台了 |
filemode | 指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效 |
format | 指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。 |
datefmt | 指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效 |
level | 指定日志器的日志级别 |
stream | 指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常 |
style | Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%' |
handlers | Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。 |
logging模块中定义好的可以用于format格式字符串说明
字段/属性名称 | 使用格式 | 描述 |
---|---|---|
asctime | %(asctime)s | 将日志的时间构造成可读的形式,默认情况下是‘2016-02-08 12:00:00,123’精确到毫秒 |
name | %(name)s | 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger |
filename | %(filename)s | 调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀 |
funcName | %(funcName)s | 由哪个function发出的log, 调用日志输出函数的函数名 |
levelname | %(levelname)s | 日志的最终等级(被filter修改后的) |
message | %(message)s | 日志信息, 日志记录的文本内容 |
lineno | %(lineno)d | 当前日志的行号, 调用日志输出函数的语句所在的代码行 |
levelno | %(levelno)s | 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) |
pathname | %(pathname)s | 完整路径 ,调用日志输出函数的模块的完整路径名,可能没有 |
process | %(process)s | 当前进程, 进程ID。可能没有 |
processName | %(processName)s | 进程名称,Python 3.1新增 |
thread | %(thread)s | 当前线程, 线程ID。可能没有 |
threadName | %(thread)s | 线程名称 |
module | %(module)s | 调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名 |
created | %(created)f | 当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值 |
relativeCreated | %(relativeCreated)d | 输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数 |
msecs | %(msecs)d | 日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上 |
说明
1. logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。
2. 如果要记录的日志中包含变量数据,可使用一个格式字符串作为这个事件的描述消息(logging.debug、logging.info等函数的第一个参数),然后将变量数据作为第二个参数*args的值进行传递,如:
logging.warning('%s is %d years old.', 'Tom', 10),
输出内容为
WARNING:root:Tom is 10 years old.
日志模块使用方式2 日志流处理流程
日志流处理流程是一个模块级别的函数是logging.getLogger([name])(返回一个logger对象,如果没有指定名字将返回root logger)。
logging日志模块四大组件:
在介绍logging模块的日志流处理流程之前,我们先来介绍下logging模块的四大组件:
组件名称 | 对应类名 | 功能描述 |
---|---|---|
日志器 Logger | 提供了应用程序可一直使用的接口 | |
处理器 | Handler | 将logger创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
logging模块就是通过这些组件来完成日志处理的,上面所使用的logging模块级别的函数也是通过这些组件对应的类来实现的。
这些组件之间的关系描述:
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
- 不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
(1) Handler类:
Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:
- 把所有日志都发送到一个日志文件中;
- 把所有严重级别大于等于error的日志发送到stdout(标准输出);
- 把所有严重级别为critical的日志发送到一个email邮件地址。这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置。
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略``Handler.setFormatter():给这个handler选择一个格式``Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个``filter``对象
需要说明的是,应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为。下面是一些常用的Handler:
Handler | 描述 |
---|---|
logging.StreamHandler | 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。 |
logging.FileHandler | 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handlers.HTTPHandler | 将日志消息以GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler | 将日志消息发送给一个指定的email地址 |
logging.NullHandler | 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。 |
(2) Formater类:
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。
Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt``=``None``, datefmt``=``None``, style``=``'%'``)
可见,该构造方法接收3个可选参数:
- fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
- datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
- style:Python 3.2新增的参数,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'
一般直接用logging.Formatter(fmt, datefmt)
(3) Filter类(暂时了解)
Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。该类定义如下:
class logging.Filter(name='')
filter(record)
比如,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。
filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。
说明:
- 如果有需要,也可以在filter(record)方法内部改变该record,比如添加、删除或修改一些属性
- 我们还可以通过filter做一些统计工作,比如可以计算下被一个特殊的logger或handler所处理的record数量等。
日志流处理简要流程:
1、创建一个logger
2、设置下logger的日志的等级
3、创建合适的Handler(FileHandler要有路径)
4、设置下每个Handler的日志等级
5、创建下日志的格式
6、向Handler中添加上面创建的格式
7、将上面创建的Handler添加到logger中
8、打印输出logger.debug\logger.info\logger.warning\logger.error\logger.critical
示例
# 代码
import logging
def log():
#创建logger,如果参数为空则返回root logger
logger = logging.getLogger("nick")
logger.setLevel(logging.DEBUG) #设置logger日志等级
#这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志
if not logger.handlers:
#创建handler
fh = logging.FileHandler("test.log",encoding="utf-8")
ch = logging.StreamHandler()
#设置输出日志格式
formatter = logging.Formatter(
fmt="%(asctime)s %(name)s %(filename)s %(message)s",
datefmt="%Y/%m/%d %X"
)
#为handler指定输出格式
fh.setFormatter(formatter)
ch.setFormatter(formatter)
#为logger添加的日志处理器
logger.addHandler(fh)
logger.addHandler(ch)
return logger #直接返回logger
logger = log()
logger.warning("泰拳警告")
logger.info("提示")
logger.error("错误")
logger.debug("查错")
或
//导入包
import logging
logger = logging.getLogger()
#设置logger对象默认级别
logger.setLevel(logging.DEBUG)
#构建两个handler流
#文件流对象
fh = logging.FileHandler("test.log",encoding="utf-8")
#输出流对象
ch = logging.StreamHandler()
#将两个流设置给logger对象
logger.addHandler(fh)
logger.addHandler(ch)
#格式化
format1 = logging.Formatter(
fmt="%(asctime)s %(name)s %(filename)s %(message)s",
datefmt="%Y/%m%d %X"
)
format2 = logging.Formatter(
fmt="%(asctime)s %(name)s ::: %(message)s",
datefmt="%Y/%m%d %X"
)
#给输出流确定格式
fh.setFormatter(format1)
ch.setFormatter(format2)
# 使用logger对象进行日志记录
logger.debug("logger debug信息")
logger.info("logger info信息")
logger.warning("logger warning信息")
logger.error("logger error信息")
logger.critical("logger critical信息")
time时间模块
time 时间模块
三种时间形式。
时间戳,
“年-月-日”
时间元组(年,月,日)
时间几种形式转换
导入包
import time
time模块参数
time.strftime()
以字符串展示时间
time.localtime()
本地时间
time.gmtime()
国标时间,国际标准时间
time.asctime([t])
作用:将struct_time类型的时间转换为如下形式:'Sun Jun 20 23:21:05 1993'
参数:struct_time类型或tuple类型的时间,不填参数的话默认为time.localtime()得到的时间。
返回值:'Sun Jun 20 23:21:05 1993'类型的时间。
time.ctime([secs])
作用:将时间戳的时间转换为表示本地时间的字符串。如果没有提供secs或没有提供secs,则使用time()返回的当前时间。
参数:时间戳类型的时间,如果不填默认为当前时间的时间戳。
返回值:格式化类型的时间,例如'Mon Mar 18 23:56:35 2019'。
time.sleep(secs)
作用:在给定的秒数内挂起调用线程的执行。
参数:秒数,参数可以是一个浮点数,表示更精确的睡眠时间。
查看时间
当前时间戳 float类型
import time
print(time.time()) #Unix开始时间到现在过了多少秒
当前时间字符串
print(time.strftime("%Y/%m%d %H:%M"))
print(time.strftime("%Y-%m-%d %X"))
当前时间元组
print(time.localtime())
print(type(time.localtime()))
t = time.localtime()
print(t[0])
print(t[1])
打印当前国标时间
print(time.gmtime())
时间转换
ret = time.localtime(3600*24) #本地时间开始时间过1天后
gm = time.gmtime(3600*24) #本地时间开始时间过1天后 3600s为1小时
print(ret)
print(gm)
print(3600/60/60)
ret2 = time.strftime("%Y-%m-%d %H:%M",ret)
print(ret2)
gm2 = time.strftime("%Y-%m-%d %H:%M",ret)
print(gm2)
print(time.asctime())
print(time.ctime())
datetime 时间模块
导入模块
import datetime
date类型
只限制到年月日
#格式化时间
import datetime
ret = datetime.date(2020,12,3)
print(ret)
print(type(ret))
print(ret.year)
print(ret.month)
print(ret.day)
time类型
仅限于时分秒
ret2 = datetime.time(12,23)
print(ret2)
print(ret2.hour)
print(ret2.minute)
datetime类型
包含年月日时分秒时间类型
ret3 = datetime.datetime(2020,12,13)
print(ret3)
print(type(ret3))
当前时间
now = datetime.datetime.now()
print(now)
print(now.year)
print(now.second)
print(now.strftime("%Y-%m-%d %X"))
strptime()时间类型转换
from datetime import datetime
first_date = datetime.strptime('2020-10-10','%Y-%m-%d')
print(first_date)
结果:
2020-10-10 00:00:00
timedelta类型
时间间隔,即两个时间点之间的长度
delta = datetime.timedelta(days=3)
now = datetime.datetime.now()
print(delta + now) #输出三天后的日期是啊金
print((now - delta).strftime("%Y/%m/%d %X")) #格式化输出3天前日期时间
s
实参 | 含义 |
---|---|
%A | 星期的名称 |
%B | 月份名 |
%m | 用数字表示的月份(01-12) |
%d | 用数字表示月份中的一天(01-31) |
%Y | 四位数的年份,如2020 |
%y | 两位数的年份,如20 |
%H | 24小时制的小时数(00-23) |
%I | 12小时制的小时数(01-12) |
%p | am或者pm |
%M | 分钟数(00-59) |
%S | 秒数 (00~60) |
random 随机数模块
#导入包
from random import randint
随机整数
n = randint(1,100) #随机产生1-100的随机数
或者
c_choice = random.randint(1,100)
random() 类型
随机浮点数
print(random.random())
randint() 类型
随机整数
print(random.randint(1,6))
choice() 类型
从序列元素中随机选取一个
print(random.choice([1,2,3,4]))
sample() 类型
从序列元素中随机选取n个
print(random.sample([1,2,3,4,5],2))
uniform() 类型
从元素中取随机浮点数
print(random.uniform(1,3))
shuffle() 类型
将顺序打乱
qq = [1,2,3,4,5,6,7]
random.shuffle(qq)
print(qq)
生成5位随机验证码
import random
def v_vode():
code = ''
for i in range(5):
num = random.randint(0,9) #生成0-9数字
upper = chr(random.randint(65,90)) #生成A~Z字母
lower = chr(random.randint(96,122)) #生成a~z字母
code += random.choice([str(num),upper,lower]) #从序列中随机选取一个数
return code
print(v_vode())
base64加密/解密
调用
import base64
关于Base64编码格式提供了以下六种接口,便于高效灵活地实现需要的编解码工作。
1. b64encode(s, altchars=None)
2. b64decode(s, altchars=None)
3. standard_b64encode(s)
4. standard_b64decode(s)
5. urlsafe_b64encode(s)
6. urlsafe_b64decode(s)
其中以"encode"结尾的方法用于将二进制串转为base64编码格式的字符串,以“decode”结尾的方法用于将base64格式的字符串重新转为二进制串。
-
我们详细查看前两个方法,注意到b64encode()和b64decode()接收同样形式的参数。其中 s 是要编/解码的字符串;默认参数altchars的可选值必须是长度至少两字节的字符串(第二个字符后的内容将被忽略),该方法表示在编/解码过程中将使用参数altchars中的前两个字符替换标准Base64字符集中的'+'和'/'。
-
因此方法3和4中的base64.standard_b64encode(s)和base64.standard_b64decode(s)等价于base64.b64encode(s)和base64.b64decode(s)。
-
而方法5和6中的base64.urlsafe_b64encode(s)和base64.urlsafe_b64decode(s)分别等价于base64.b64encode(s , '-')和base64.b64decode(s , '-'),即在编/解码过程中使用'-'和'_'替代标准Base64字符集中的'+'和'/',生成可以在URL中使用的Base64格式文本。
使用示例:
import base64
#将内容转换为二进制,base64编码
ll = "hello"
base = base64.b64encode(ll.encode())
print(base) #b'aGVsbG8='
#base64解码,然后二进制内容转换为文本
qq = base64.b64decode(base)
print(qq.decode()) #'hello'
本模块还提供了Base32和Base16编解码接口:
Base16编解码:
qw = 's'
base = base64.b32encode(qw.encode())
print(base) #b'OM======'
base = base64.b32decode(base)
print(base.decode()) #s
Base16编解码:
qw = 's'
base = base64.b16encode(qw.encode())
print(base) #b'73'
base = base64.b16decode(base)
print(base.decode()) #s
遇见的报错问题
AttributeError: module 'hashlib' has no attribute 'md5' 报错
import hashlib
hd = hashlib.md5()
s1 = "li".encode()
hd.update(s1)
print(hd.hexdigest())
在练习hashlib 模块的时候,遇到的一个问题,因为敲的是跟课堂上一模一样的代码,然后百思不得其解,怀疑到python版本上来了,google发现问题的根源在于我的脚本文件命名与python内置模块重名导致的。
原因
python在执行程序的时候,会先将当前目录下的hashlib导入,正好是我取得文件名是hashlib。程序会将我创建的hashlib导入进入,而找不到里面定义的功能模块,所以会报错
标签:logging,模块,print,path,日志,os
From: https://www.cnblogs.com/megshuai/p/18517563