什么是模块
模块是一系列功能的集合体
常见的模块形式(自定义模块、第三方模块、内置模块):
1、一个module.py文件就是一个模块,文件名是module.py,而模块名是module
2、一个包含有__init__.py文件的文件夹也是模块
3、已被编译为共享库或DLL的C或C++扩展
4、使用C编写并链接到python解释器的内置模块
为什么要用模块
1、用第三方或者内置的模块是一种拿来主义,可以极大地提升开发效率
2、自定义模块即将我们自己程序中需要用到的公共的功能写入一个python文件
然后程序的各部分组件可以通过导入的方式来引用/重用自定义模块中的功能
如何用模块
大前提:模块是被执行文件导入使用,模块的导入必须搞明白谁是执行文件,谁是被导入的模块
import
首次import m1导入模块都发生三件事:
1、先创建一个模块的名称空间
2、执行m1.py,将执行过程中产生的名称都放入模块的名称空间中
3、在当前执行文件中拿到一个名字m1,该名字是指向模块的名称空间的
使用方法:指名道姓地访问m1名称空间中的名字func,优点是不会与当前名称空间中的名字冲突,缺点是每次访问都需要加上前缀
m1.func
from ... import
首次from m1 import func导入模块都发生三件事:
1、先创建一个模块的名称空间
2、执行m1.py,将执行过程中产生的名称都放入模块的名称空间中
3、在当前执行文件中直接拿到一个功能名func,该名字是直接指向模块名称空间中的某一个功能的
使用方法:直接使用功能即可,优点是无需加任何前缀,缺点是容易与当前名称空间中的名字冲突
def func():
pass
func()
导入模块的两种方式
绝对导入:
参照执行文件的sys.path为基准查找,既可以当作执行文件使用也可以当作被导入的模块使用
但文件当作模块使用必须参照执行文件的环境变量来导入
格式为 from 文件夹 import 模块名
相对导入:
. 以当前文件的文件夹为基准查找,只能被当作模块导入,不能当作执行文件执行
from . import 模块名
相对导入一般用在导入包的情况下,
在导入包的情况下,有3种限制
1 .的方式不能出包的范围
2 .的方式不能当作执行文件执行
3 .的方式前提只能是包
模块的搜索路径
内存-》内置模块-》sys.path
import logging 日志模块
进行基本的日志配置:
logging.basicConfig(filename='access.log',
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
level=10,
# stream=True
)
日志级别遵循原则:自下而上进行匹配 #debug-》info-》warning-》error-》critical:
logging.debug('调试信息') #10
logging.info('正常信息') #20
logging.warning('不好啦着火啦') #30
logging.error('报错信息') #40
logging.critical('严重错误信息') #50
问题:
1、没有指定日志级别
2、没有指定日志格式
3、只能往屏幕打印,没有写入文件
新问题 #1、不能指定字符串编码 #2、只能往文件中打印
import logging logging模块包含四种角色:logger,filter,formatter,handler
-
1、logger:负责产生日志信息 logger1=logging.getLogger('交易日志') logger2=logging.getLogger('用户相关')
-
2、filter:负责筛选日志
-
3、formatter:控制日志输出格式 formatter1=logging.Formatter( fmt='%(asctime)s:%(name)s:%(levelname)s:%(message)s', datefmt='%Y-%m-%d %X' ) formatter2=logging.Formatter( fmt='%(asctime)s:%(message)s', datefmt='%Y-%m-%d %X' )
-
4、handler:负责日志输出的目标 h1=logging.FileHandler(filename='a1.log',encoding='utf-8') h2=logging.FileHandler(filename='a2.log',encoding='utf-8') sm=logging.StreamHandler()
-
5、绑定logger对象与handler对象 logger1.addHandler(h1) logger1.addHandler(h2) logger1.addHandler(sm)
-
6、绑定handler对象与formatter对象 h1.setFormatter(formatter1) h2.setFormatter(formatter1) sm.setFormatter(formatter2)
-
7、设置日志级别:可以在两个关卡进行设置logger与handler:
logger1.setLevel(10) h1.setLevel(10) h2.setLevel(10) sm.setLevel(10) logger1.info('Egon借给李杰100W')
最终的用法:
LOG_PATH = os.path.join(BASE_DIR, 'log', 'user.log')
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(DB_PATH):
os.mkdir(DB_PATH)
# log文件的全路径
# logfile_path = os.path.join(DB_PATH, 'log')
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': LOG_PATH, # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
},
},
}
import logging.config
def get_logger(name):
"""
日志格式
:return:
"""
logging.config.dictConfig(settings.LOGGING_DIC)
logger = logging.getLogger(name) # 这里的name 指的文件名
return logger
# json 模块
import json
with open('db1.json','rt',encoding='utf-8') as f:
json.load(f) # load用在文件的读,反序列化
json.loads('{"name":"egon"}') # loads用在字典的序列化,
with open('db.json','wt',encoding='utf-8') as f:
l=[1,True,None]
json.dump(l,f) # dump用在 序列化到文件中
x = json.dumps('name': 'egon') #dumps用在序列化
用json反序列化
with open('db.json','rt',encoding='utf-8') as f:
l=json.load(f)
print(l)
# pickle模块
import pickle
## 反序列化
1、从文件中读取pickle格式
with open('db.pkl','rb') as f:
pkl=f.read()
2、将json_str转成内存中的数据类型
dic=pickle.loads(pkl)
print(dic['a'])
1和2可以合作一步
with open('db.pkl','rb') as f:
dic=pickle.load(f)
print(dic['a'])
## 序列化
dic={'a':1,'b':2,'c':3}
1 序列化
pkl=pickle.dumps(dic)
print(pkl,type(pkl))
2 写入文件
with open('db.pkl','wb') as f:
f.write(pkl)
1和2可以合作一步
with open('db.pkl','wb') as f:
pickle.dump(dic,f)
```
# 对比json 和pickle
* 什么是序列化/反序列化
序列化就是将内存中的数据结构转换成一种中间格式存储到硬盘或者基于网络传输
发序列化就是硬盘中或者网络中传来的一种数据格式转换成内存中数据结构
* 为什么要有
1、可以保存程序的运行状态
2、数据的跨平台交互
* json
优点:
跨平台性强
缺点:
只能支持/对应python部分的数据类型
* pickle
优点:
可以支持/对应所有python的数据类型
缺点:
只能被python识别,不能跨平台
# time模块
## 时间分为三种格式
### 1、时间戳
start= time.time()
time.sleep(3)
stop= time.time()
print(stop - start)
输出结果:
3.000129222869873
### 2、格式化的字符串形式
print(time.strftime('%Y-%m-%d %X'))
print(time.strftime('%Y-%m-%d %H:%M:%S %p'))
输出结果:
2018-07-30 18:04:27
2018-07-30 18:04:27 PM
### 3、 结构化的时间/时间对象
t1=time.localtime()
print(t1)
print(type(t1.tm_min))
print(t1.tm_mday)
t2=time.gmtime()
print(t1)
print(t2)
获取格式化字符串形式的时间麻烦
时间戳与格式化时间之间的转换麻烦
获取之前或者未来的时间麻烦.所以用到了 datetime模块
# datetime模块
print(datetime.datetime.now()) # 现在的时间
print(datetime.datetime.fromtimestamp(1231233213))
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 现在的时间加上未来的3天
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 现在的时间减去3天前
s=datetime.datetime.now()
print(s.replace(year=2020)) # 修改年份
# random 模块
import random
print(random.random())#(0,1)----float 大于0且小于1之间的小数
print(random.randint(1,3)) #[1,3] 大于等于1且小于等于3之间的整数
print(random.randrange(1,3)) #[1,3) 大于等于1且小于3之间的整数
print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716
item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
print(item)
## 产生随机号码
def make_code(size=7): res = '' for i in range(size): # 循环一次则得到一个随机字符(字母/数字) s = chr(random.randint(65, 90)) num = str(random.randint(0, 9)) res += random.choice([s, num]) return res
res=make_code() print(res)
# 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的大小
# 打印进度条
import time
def make_progress(percent,width=50): if percent > 1: percent=1 show_str=('[%%-%ds]' % width) % (int(percent * width) * '#') print('\r%s %s%%' %(show_str,int(percent * 100)),end='')
total_size=1025 recv_size=0 while recv_size < total_size: time.sleep(0.1) # 模拟经过了0.5的网络延迟下载了1024个字节 recv_size+=1024 # 调用打印进度条的功能去打印进度条 percent=recv_size / total_size make_progress(percent)
# re模块
![](http://i2.51cto.com/images/blog/201808/01/9e03e24d7b3b0e9586f32fec41af4b2e.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
-----
pattern=re.compile('alex')
print(pattern.findall('alex is SB,alex is bigSB')) # 匹配所有的'alex'
print(pattern.search('alex is SB,alex is bigSB').group()) # 从行首开始匹配,没有就返回None.有就可以通过group拿到
print(re.match('alex','123alex is SB,alex is bigSB'))#以什么结尾,有就返回这个值,没有就返回None
结果如下:
['alex', 'alex']
alex
None
-----
# subprocess模块
import subprocess
obj=subprocess.Popen( 'tasklist', shell=True, stdout=subprocess.PIPE, # 正确的管道 stderr=subprocess.PIPE # 错误的管道
) print(obj) stdout_res = obj.stdout.read() # 得到正确管道中的数据 print(stdout_res.decode('gbk')) print(stdout_res)
stderr_res1=obj.stderr.read() #得到错误管道中的数据 stderr_res2=obj.stderr.read() # 管道中的信息只能取一次 stderr_res3=obj.stderr.read() print(stderr_res1.decode('gbk')) print(stderr_res1) print(stderr_res2) print(stderr_res3)
# hash模块
* 什么是hash
hash是一种算法,该算法接受传入的内容,经过运算得到一串hash值
如果把hash算法比喻为一座工厂
那传给hash算法的内容就是原材料
生成的hash值就是生产出的产品
* 为何要用hash算法
hash值/产品有三大特性:
1、只要传入的内容一样,得到的hash值必然一样
2、只要我们使用的hash算法固定,无论传入的内容有多大,
得到的hash值的长度是固定的
3、不可以用hash值逆推出原来的内容
基于1和2可以在下载文件时做文件一致性校验
基于1和3可以对密码进行加密
* 怎么用
import hashlib
1、造出hash工厂
m=hashlib.sha512('你'.encode('utf-8'))
2、运送原材料
m.update('好啊美sadfsadf丽asdfsafdasdasdfsafsdafasdfasdfsadfsadfsadfsadfasdff的张铭言'.encode('utf-8'))
3、产出hash值
print(m.hexdigest()) #2ff39b418bfc084c8f9a237d11b9da6d5c6c0fb6bebcde2ba43a433dc823966c
# import shutil #压缩模块
with open('old.xml','r') as read_f,open('new.xml', 'w') as write_f:
shutil.copyfileobj(read_f,write_f)
shutil.make_archive("data_bak", 'gztar', root_dir='D:\SH_fullstack_s2\day04')
# import tarfile # 解压
t=tarfile.open('data_bak.tar.gz','r')
t.extractall('D:\SH_fullstack_s2\day20\dir')
t.close()
-----
# import xml
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml") # 打开一个xml的文件 root = tree.getroot() # 用getroot 拿到树根
对于任何标签都有三个特征:标签名、标签属性、标签的文本内容 print(root.tag)# 标签名 print(root.attrib)# 标签属性 print(root.text)# 文本内容
print(list(root.iter('year'))) #全文搜索,找到所有 for year in root.iter('year'): print(year.tag) print(year.attrib) print(year.text) print('='*100)
print(root.find('country').attrib) #在root的子节点找,只找一个 print([country.attrib for country in root.findall('country')]) #在root的子节点找,找所有
root.iter('year') #全文搜索 root.findall('country') # 在root的子节点找,找所有 root.find('country') # 在root的子节点找,只找一个
1、查 遍历整个文档 for country in root: print('============>国家 %s' %country.attrib) for item in country: print(item.tag) print(item.attrib) print(item.text)
2、改 for year in root.iter('year'): print(year.tag) year.attrib={'updated':'yes'} year.text=str(int(year.text)+1)
tree.write('a.xml')
3、增 for country in root: rank=country.find('rank') if int(rank.text) > 50: # print('符号条的国家',country.attrib) tag=ET.Element('egon') tag.attrib={'updated':'yes'} tag.text='NB' country.append(tag)
tree.write('a.xml') 4、删
for country in root: tag=country.find('egon') #print(tag,bool(tag)) if tag is not None: print('====>') country.remove(tag) tree.write('a.xml')
# import configparser # 解析文件模块
config = configparser.ConfigParser() config.read('config.ini') # a.cfg a.ini a.cnf
print(config.sections()) # 去除标题 print(config.options('egon')) # 把这个目录下的k取出来 print(config.items('egon')) # 取出 这个目录下的所有,放到元组中
res = config.get('egon','age') res = config.getint('egon', 'age') print(res, type(res))
res = config.getfloat('egon', 'salary') print(res, type(res))
res = config.getboolean('egon', 'is_beautiful') print(res, type(res))
输出结果如下: ['egon', 'alex'] ['pwd', 'age', 'sex', 'salary', 'is_beautiful'] [('pwd', "'123'"), ('age', '18'), ('sex', "'male'"), ('salary', '3.1'), ('is_beautiful', 'True')] 18 <class 'int'> 3.1 <class 'float'> True <class 'bool'>