模块
概念
Python 模块(Module),其本质是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。
模块的作用
模块能定义函数,类和变量,模块里也能包含可执行的代码。在其他python程序中可以导入模块,使用模块中的函数或类等,避免代码的重复编写,也加强代码的逻辑结构.
导入整个模块
工作流程
1、先从sys.modules()中寻找模块是否已经导入;如果没有就在sys.path中寻找模块;
注意
import sys
sys.path.append(os.path.dirname(os.getcmd()))
#将本程序目录添加为sys.path,便于后面导入自己编写的模块文件
2、找到模块,创建模块的专属内存空间,将模块中的代码读到专属内存空间(与程序的内存空间独立);
3、在新创建的命名空间中执行模块中包含的代码,模块中的代码只在第一次遇到导入import语句时才执行.
导入格式
- import 文件名字(不含.py)
- import 文件名字(不含.py) as 别名
将模块重命名,方便调用较长的模块名
调用模块中函数(变量)
模块名.函数名(变量名)
time.time()
函数名(变量名)不会和本身程序文件的相同函数名(变量名)冲突,属于两个独立内存空间
导入顺序
1、内置模块 2、扩展模块 3、自定义模块
一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。
多个模块的import 建议分多行
导入模块中的函数(变量)
格式:
from 模块名 import 目标名字
from 模块名 import *
对比import 模块名,会将模块文件的名称空间带到当前名称空间中,使用时必须是模块名.名字的方式.而from 语句相当于import,也会创建新的名称空间,但是将模块中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了
在模块中__all__是一个列表,如果没有该列表,则import *时会导入模块的所有变量和函数,有则导入列表__all__中指定的。
调用模块中函数
可直接函数名 无需先加上模块名.
此种方式导入指定的名字,相对的更加节省内存空间,但导入的变量名或函数名会和本文件的冲突
特殊变量__name__
在py文件中有一个特殊变量__name__,当直接执行该py时,name__的值为__main;当该py文件被做模块引用时,__name__变的值为模块名(即py文件名)。
在.py文件中有部分代码A只有在文件自己执行而使用,被作为模块import时不执行这部分代码。那么就可以利用上面的特性__name__来实现;
if __name__=__main__:
部分代码A
常用模块
re模块
re模块是让python也具备正则表达式功能,先简单的介绍下正则表达式
字符组
字符可以是数字、字母、标点符号等。用[]将字符组成一个组,匹配组内的任一字符。
[0-9]:匹配任一个数字
[a-z]:匹配任一个小写字母
[0-9a-z]:匹配任一数字和小写字母
字符匹配
. 匹配除换行符外的任一单个字符
\w 匹配单个字母或数字或下划线
\s 匹配任意个数的空白符 包括tab
\d 匹配单个数字
\n 匹配一个换行符
\t 匹配一个制表符 tab
位置匹配
通常紧跟在匹配内容的后面,用于说明匹配内容出现的位置
\b 表示前面匹配项出现在单词结尾 g\b
^ 表示后面的匹配项出现在行首 ^a
$ 表示前面的出现在行尾 c$
数量匹配
通常紧跟在匹配内容的后面,用于说明匹配内容出现的次数
* 前面的匹配零次或更多次
+ 前面的匹配项至少出现一次
? 前面的匹配项出现0或1次
{n} 前面的匹配项出现n次
{n,} 前面的匹配项至少出现n次
{,n} 前面的匹配项至多出现n次
*,+,?等数量匹配使用时都是贪婪匹配(也就是尽可能多匹配后面内容),但如果在他们后面再加个?,就成了惰性匹配
举例
name='pksa1231b32b'
/a.*b/ 贪婪匹配结果a1231b32b
/a.*?b/ 惰性匹配结果a1231b
特殊匹配
[……] 匹配中括号内的单个字符
[^……] 匹配非中括号内的单个字符
分组
(正则表达式)
将匹配中的内容列为分组供后面的表达式调用,\1表示第一个括号中的匹配内容
(\d)abc\1 1abc1符合此表达式 1abc2不符合表达式
特别注意
正则表达式使用 \ 对特殊字符进行转义,比如,为了匹配字符串 ‘python.org’,我们需要使用正则表达式 ‘python.org’,而 Python 的字符串本身也用 \ 转义,所以上面的正则表达式在 Python 中应该写成 ‘python\.org’,这会很容易陷入 \ 的困扰中,因此,我们建议使用 Python 的原始字符串,只需加一个 r 前缀,上面的正则表达式可以写成:r’python.org’
正则表达式-re模块
re 模块的一般使用步骤如下:
- 使用 compile 函数将正则表达式的字符串形式编译为一个 Pattern 对象
- 通过 Pattern对象提供的一系列方法对文本进行匹配查找,获得匹配结果(一个 Match 对象)
- 最后使用 Match对象提供的属性和方法获得信息,根据需要进行其他的操作
compile 函数用于编译正则表达式,生成一个 Pattern 对象,使用形式如下
re.compile(pattern[, flag])
举例
import re
# 将正则表达式编译成 Pattern 对象
pattern1 = re.compile(r'\d+')
Pattern 对象的一些常用方法主要有:
- match 方法
- search 方法
- findall 方法
- finditer 方法
- split 方法
- sub 方法
- subn 方法
以上方法使用方式与re模块中的函数基本相近。
match方法
从指定的位置范围进行匹配,指定的开头必须能被匹配否则就是匹配失败,匹配到第一个结果就返回Match对象,使用格式如下
match(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,pos和endpos分别默认是0和len(string)
import re
pattern = re.compile(r'\d+')
m = pattern.match('aone123twothree34four') #从0位置开始,0位置是字符a匹配\d+失败
print(m) #输出None
a= pattern.match('aone123twothree34four', 4, 10) #从4位置开始,4位置是数字1匹配成功,向后贪婪匹配得获得Match对象
print(a) #输出<re.Match object; span=(3, 6), match='123'>
print(a.group()) #group方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0)。group(1)对应正则表达中第一个()
补充例子说明:
import re
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
m = pattern.match('Hello World Wide Web')
print (m) # 匹配成功,返回一个 Match 对象
<_sre.SRE_Match object at 0x10bea83e8>
>>> m.group(0) # 返回匹配成功的整个子串
'Hello World'
>>> m.span(0) # 返回匹配成功的整个子串的在匹配目标中的索引
(0, 11)
>>> m.group(1) # 返回第一个分组匹配成功的子串
'Hello'
>>> m.span(1) # 返回第一个分组匹配成功的子串的索引
(0, 5)
>>> m.group(2) # 返回第二个分组匹配成功的子串
'World'
>>> m.span(2) # 返回第二个分组匹配成功的子串
(6, 11)
>>> m.groups() # 等价于 (m.group(1), m.group(2), ...)
('Hello', 'World')
>>> m.group(3) # 不存在 输出失败
search方法
search 方法用于在指定位置内查找字符串的位置,它也是一次匹配,只要找到了第一个匹配的结果就返回为Match对象。匹配失败则返回None,使用格式如下
search(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,pos和endpos分别默认是0和len(string)
示例
import re
pattern = re.compile(r'\d+')
a = pattern.search('aone123twothree34four',7)
print(a)
print(a.group()) #输出34
print(a.span()) #输出(15,17)
findall方法
在指定位置内查找匹配结果,将所有匹配内容放入一个列表中返回。
findall(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,pos和endpos分别默认是0和len(string)
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print (result1) #['123456', '789']
print (result2) #['1', '2']
finditer 方法
功能和findall方法基本一样,只是匹配的获得为Match对象存放在一个迭代器内。
import re
pattern = re.compile(r'\d+')
result_iter1 = pattern.finditer('hello 123456 789')
print (type(result_iter1))
#123456和789两个Match对象组成了一个迭代器
print (result_iter1)
for m1 in result_iter1: # m1 循环获取 Match 对象
print ('matching string: {}, position: {}'.format(m1.group(), m1.span()))
split方法
按照能够匹配内容对字符串进行分割,放入列表中返回,使用格式
split(string[, maxsplit])
其中,maxsplit 用于指定最大分割次数,不指定将全部分割。
import re
p = re.compile(r'[\s\,\;]+') #能匹配的内容为, 多个空格 多个;
print (p.split('a,b;; c d'))
#输出结果:['a', 'b', 'c', 'd']
sub方法
sub 方法用于替换。它的使用形式如下:
sub(repl, string[, count])
repl
如果是字符串,使用 repl 去替换字符串中的匹配的子串(受count控制),并返回替换后的字符串,另外repl 还可以使用 \id 的形式来引用分组,但不能使用编号 0;
repl
如果是函数,则将匹配的内容放入函数中处理。该函数必须能返回替换结果
count
用于指定最多替换次数,不指定时全部替换。
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123, hello 456'
#有两个匹配的内容'hello 123'和'hello 456'
print(p.sub(r'hello world', s))
# 使用 hello world' 替换 'hello 123' 和 'hello 456'
print(p.sub(r'\2 \1', s))
# 引用分组 在第一个匹配的内容 \2匹配的分组是指'123' \1匹配的分组是指'hello'
# 在第二个匹配的内容 \2匹配的分组是指'456' \1匹配的分组是指'hello'
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123, hello 456'
#有两个匹配的内容'hello 123'和'hello 456'
def func(m):
return 'hi' + ' ' + m.group(2)
print (p.sub(func, s))
print (p.sub(func, s, 1)) #只处理一个匹配的内容
subn 方法
subn 方法跟 sub 方法的行为类似,也用于替换。只是返回的结果是一个元组,元组的第一个元素是替换后的字符串,第二个元素是替换的次数
时间模块time
time.time() 返回一个以秒为单位的浮点数,该浮点数为时间戳(即从1970-01-01的0点0分0秒 开始的偏移量)
time.sleep(n) 程序休眠n秒后继续执行
time.strftime() 按括号中指定输出格式,对时间进行格式化,然后输出
输出格式:
%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)
%X 等同于%H:%M:%S
print(time.strftime("%Y-%m-%d %X")) 输出2019-02-20 15:14:48
time.locatime() 输出结构化时间,生成对象类似命名元组
时间戳与结构化时间之间的转换
t=time.time() #得到时间戳
print(time.localtime(t)) #结构化为本地时区的时间
print(time.gmtime(t)) #结构化为格林时区的时间
print(time.mktime(time.localtime(t))) #结构化时间转为时间戳
格式化时间和结构化时间之间的转换
time.strptime(“时间字符串”,“格式化规则”) #将时间字符串按格式化规则转换,得到结构化时间
time.strptime("2019-12.31","%Y-%m.%d")
time.strftime(“格式化规则”,结构化时间) #将结构化时间按格式化规则转换,得到格式化时间
time.strftime("%Y/%m/%d %X",time.localtime(t))
随机数模块random
random.random() # 大于0且小于1之间的小数
random.uniform(1,3) #大于1小于3的小数
random.randint(1,5) # 大于等于1且小于等于5之间的整数
random.randrange(1,10,2) # 大于等于1且小于10之间的奇数
random.choice([1,‘23’,[4,5]]) #列表中的元素选一个
random.sample([1,‘23’,[4,5]],2)#列表元素任意2个组合
操作系统交互模块os
os模块是与操作系统交互的一个接口
os.getcmd() 获取当前脚本的工作目录
os.chdir() 切换工作目录
os.makedirs(‘dir1/dir2’) 在当前目录下创建多层递归目录
os.removedirs(‘dir1/dir2’) 一次性删除递归的多层空目录
os.mkdir(‘dir1’) 生成单层目录
os.rmdir(‘dir1’) 删除单级空目录,若目录不为空则无法删除
os.listdir(‘dirname’) 将指定目录下的所有文件和子目录,包括掩藏的,组成一个列表返回。
os.remove() 删除一个文件
os.rename(“oldname”,“newname”) 重命名文件/目录
os.stat(‘path/filename’) 获取文件/目录信息 将这些信息组成一个命名元组返回
os.system(“bash command”) 调用程序所在系统运行shell命令,直接打印执行结果
os.popen("bash command).read() 运行shell命令,返回执行结果
import os
os.system('pwd')
a=os.popen('pwd').read()
print(a)
os.path.abspath(path) path是相对路径,返回path规范化的绝对路径
os.path.split(path) 将path按最后一个路径分割符进行分割成二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。即os.path.split(path)的第二个元素
与解释器交互模块sys
sys模块是与python解释器交互的一个接口
sys.argv 将python解释器接受的参数组成一个列表,第一个元素基本是程序名字。
import sys
a=sys.argv
print(a)
print(a[0])
print(a[1])
##调用python解释器进行 python test.py pks 123
##输出内容:
##['test.py', 'pks', '123']
##test.py
##pks
sys.exit(n) 用于退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version 获取Python解释程序的版本信息
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.modules 记录程序已加载的模块
序列化模块
序列化:将其他类型的数据(如字典,列表等)转换为字符串类型的数据
反序列化:将字符串类型还原为原来的数据类型
数据存储和网络传输,只能操作bytes类型,字典等类型的数据需要先转换为字符串类型,才能再转成bytes类型
常用序列化模块 json、pickle
json是一个众多开发语言通用的序列化格式,只有一部分数据类型能通过json转成字符串类型(数字,列表 ,字典,元组)
序列化方法:
json.dumps() 用于操作内存中的数据
json.dump() 序列化并将结果写入某个文件句柄
反序列化:
json.loads() 用于操作内存中的数据
json.load() 先从文件句柄中读取,再完成反序列化
注意
1、在进行序列化的时候json.dump和json.dumps中有一个ensure_ascii参数默认为True,会将所有非ascii码的字符进行转换,对于需要中文显示可设置为False
2、使用json.dump()和json.load()建议一次性序列化写入,并一次性读取反序列化
import json
#(1)dumps
dic = {'k1':'值1','k2':'值2','k3':'值3'}
str_dic = json.dumps(dic) #将字典转换成一个字符串
print(type(str_dic),str_dic)
'''结果:
<class 'str'> {"k3": "\u503c3", "k1": "\u503c1", "k2": "\u503c2"}
'''
str_dic1 = json.dumps(dic,ensure_ascii=False) #将字典转换成一个字符串
print(type(str_dic1),str_dic1)
'''结果:
<class 'str'> {"k1": "值1", "k2": "值2", "k3": "值3"}
'''
#(2)loads
dic2 = json.loads(str_dic) #将一个序列化转换成字典
print(type(dic2),dic2)
'''结果:
<class 'dict'> {'k3': '值3', 'k1': '值1', 'k2': '值2'}
'''
#(3)dump
f1 = open('json_file','w')
dic = {'k1':'值1','k2':'值2','k3':'值3'}
json.dump(dic,f1) #dump方法将dic字典信息,转换成json字符串写入文件
f1.close()
#(4)load
f = open('json_file')
dic2 = json.load(f) #load方法将文件中的内容转换成数据类型返回
f.close()
print(type(dic2),dic2)
'''结果:
<class 'dict'> {'k3': '值3', 'k1': '值1', 'k2': '值2'}
'''
摘要算法的模块hashlib
import hashlib
md5=hashlib.md5() ##实例化一个使用使用md5算法的摘要对象
md5.update(bytes("平时",encoding="utf-8")) ##摘要对象的update方法只能对bytes类型数据进行摘要计算
print(md5.hexdigest()) ##摘要结果存放在对象的独立内存空间中,通过hexdigest()方法获取
注意:一个对象若多次对数据进行摘要处理,其存放在对象独立内存空间中的摘要结果会一次次的改变。
import hashlib
md5 = hashlib.md5()
md4 = hashlib.md5()
md5.update(b'12345')
print(md5.hexdigest()) ##827ccb0eea8a706c4c34a16891f84e7b
md5.update(b'12345')
print(md5.hexdigest()) ##8cfa2282b17de0a598c010f5f0109e7d
md4.update(b'12345')
print(md4.hexdigest()) ##827ccb0eea8a706c4c34a16891f84e7b
#hashlib 还提供sha算法,但计算方法更复杂,消耗较大cpu
# 摘要算法通畅用于 密码的密文存储和文件的一致性验证
#为了更加安全的进行摘要加密,还可以在实例化摘要对象时,就进行加盐
import hashlib # 提供摘要算法的模块
md5 = hashlib.md5()
md5.update(b'123456')
print(md5.hexdigest())#输出e10adc3949ba59abbe56e057f20f883e
md5 = hashlib.md5(bytes('盐',encoding='utf-8')) ##盐可以是任意字符
md5.update(b'123456')
print(md5.hexdigest())#输出970d52d48082f3fb0c59d5611d25ec1e
应用举例,对文件进行摘要
import hashlib
def md5sum(filename):
"""
用于获取文件的md5值
参数 filename: 文件名
return: MD5码
"""
if not os.path.isfile(filename):
return
# 先判断文件是否存在,不存在则返回none
myhash = hashlib.md5()
f = open(filename, 'rb')
while True:
b = f.read(8096)
if not b:
break
myhash.update(b)
#循环对文件的内容进行摘要计算
f.close()
return myhash.hexdigest()
配置文件的模块
常见的配置文件,分为一个或多个节(section),每个节可以有多个参数(键=值)。举例如下:
[DEFAULT]
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
ForwardX11 = no
对于此类文件的操作python中使用configparser模块
举例一 生成此类文件
import configparser
config = configparser.ConfigParser()
# 实例化一个配置对象
config["DEFAULT"] = { 'Compression': 'yes',
'CompressionLevel': '9',
'ForwardX11':'yes'
}
# 配置对象的[DEFAULT]章节设置上面三个键值对
config['bitbucket.org'] = {'User':'hg'}
config['topsecret.server.com'] = {'ForwardX11':'no'}
with open('example.ini', 'w') as configfile:
config.write(configfile)
#将配置对象写入文件example.ini中
举例二 此类配置文件中获取信息
import configparser
config = configparser.ConfigParser()
#实例化一个配置对象
config.read('example.ini')
#配置对象读取文件example.ini作为对象内存
print(config.sections())
##打印配置对象中除DEFAULT节以外的节名
print('bytebong.com' in config)
# 判定配置对象是否存在名为bytebong.con的节,不存在返回False,存在返回True
print(config['bitbucket.org']["user"])
# 打印配置对象bitbucket.org节中键名为user的值
print(config.options('bitbucket.org'))
# 同for循环,找到'bitbucket.org'下所有键
print(config.items('bitbucket.org'))
#找到'bitbucket.org'下所有键值对
print(config.get('bitbucket.org','compression'))
#get方法,找到指定节名下的key对应的value
举例三 此类配置文件增删改
import configparser
config = configparser.ConfigParser()
#实例化一个配置对象
config.read('example.ini')
#配置对象读取文件example.ini作为对象内存
config.add_section('yuan')
#配置对象增加名为yuan的节
config.remove_section('bitbucket.org')
#配置对象删除名为bitbucket.org的节
config.remove_option('topsecret.server.com',"forwardx11")
#配置对象删除名为topsecret.server.com节中key名为forwardx11的键值对
config.set('topsecret.server.com','k1','11111')
#配置对象设置topsecret.server.com节中键值对k1=11111
config.write(open('new2.ini', "w"))
#将配置对象写入文件new2.ini中
日志模块
为了方便进行debug,任何程序必须有专门的日志输出.
python中logging是专门的日志模块
日志的等级由低到高如下:
CRITICAL > ERROR > WARNING > INFO > DEBUG
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志
日志模块的使用分为基础配置和对象配置两种
== 基础配置==
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
#创建一个文件操作符
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[file_handler,],
# 定义日志输出的处理方式handlers
level=logging.ERROR
)
#定义一个日志基础配置
logging.error('你好')
#使用日志基础配置输出error级别的日志信息"你好"
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
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用户输出的消息
日志的对象配置
logger的基础配置,缺点是不能将log信息即输出到屏幕又输出到文件,而日志的对象配置恰好能解决此缺点
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')
包
包是一种通过使用格式:‘.模块名’
来组织python模块名称空间的方式。包的本质是一个目录,该目录下存放着格式各样的模块文件,以此来提高程序的结构性和可维护性.
包的导入
包相关的导入语句也分为import和from … import …两种,但是无论哪种,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。对于from ... import ...形式的导入,点号只能出现在from后不可以用于import