python学习笔记 第六章 模块

第六章 模块

  • 什么是模块
    • 本质是一个py文件,对程序员直接提供某方面功能的文件
  • 什么是包
    • 文件夹,存储多个py文件
    • 如果

模块的本质:放在类库中lib中的py文件,一般要import才能使用

模块分为三种,一种是内置模块,已经下载好的。另一种 就是第三方模块,需下载安装使用。还有一种是自己写的,叫做自定义模块。

常见的内置模块:json / datetime / os / sys

常见的第三方模块:requests / xlrd

同理导入模块的路径一般也分为三种

#第一种,python安装目录下
import hashlib   #内置函数
#D:\Python\Lib  

#第二种,python下载的第三方模块
import xlrd    #第三方模块
#D:\Python\Lib\site-packages

#第三种,python当前运行的目录
import xxx   #自定义模块

如果找不到上面的路径可以用sys.path查看

import sys
primt(sys.path,type(sys.path))  #以列表的形式展现出来
for i in sys.path:
    print(i)

#注意如果是在pycharm中运行,会多出两个路径,一个是运行pycharm的环境,一个是多出的当前的py文件所在路径

#所以 想要添加导入模块的路径可以用sys.path.append()
sys.path.append("D:\\")

配置环境变量,首先环境变量设置为python安装的目录,再添加一条具体到Scripts的路径。

在cmd中即可使用ipip intall 模块 进行下载和安装,安装成功以后可以在Lib\site-packages找到。

pip 是在Scipts的程序,若需要更新,可以使用提示的更新代码进行更新pip程序。

#当然,也可以自定义模块
#即写一个python文件,然后其他的python文件就能导入这个python文件,使用里面的函数。
import xxx
xxx.func()
#导入模块,会运行模块中的内容并加载到内存中
import xxx

#调用模块的函数 
xxx.func()

#另一种导入的方式,同样也需要加载到内存中    from 模块  import 函数
from xxx import func
func()  #可以直接使用

#也可以同时导入多个函数 
from xxx import func,show

#但是如果当前运行的文件有相同的函数名,可以将函数重新命名
from xxx import func as f
f()

from xxx import *  #导入类库中所有的函数  

#相对导入,不推荐
from . import xxx
from .. import xxx  #再往.的上级去找
#注意,文件和文件夹的命名不能是导入的模块名称相同,否则就会直接在当前的目录中查找

总结:

  • 模块和要执行的py文件在同一个目录,且需要很多的功能的时候,推荐用import
  • 其他推荐:from 模块 import 函数 使用:函数()

模块的调用:

#当嵌套 多层的情况下,如一个包里含有多个py文件,现在需要带入py文件中的函数
#方式一:
import 包.py文件名
包.py文件名.func()

#方式二
from 包 import py文件名
py文件名.func()

#方式三
from 包.py文件名 import func
func()
#发现无论嵌套多少层,from 模块 import 函数/py文件名  都还是适用

注意:import 只能导入sys.path的文件,只要不是内置模块 或者第三方模块,且不在当前执行目录下,就不能使用

实在需要使用要append到sys.path中

而且在 pycharm中会自动将项目所在的目录添加到sys.path中,所以pycharm中能够运行同一个项目下面的另一个包中的模块,而在其他的程序中则不能这样使用。若一定要用,则需要将项目的目录添加到 sys.apth中。

#如果在不同的电脑中,这个项目的路径是不一样的,那么代码需要修改
#print(_file_)   #找到当前文件的目录,但是在python解释器中是一个参数
import os
import sys
v=os.path.abspath(_file_)

#再往上一级去找文件的绝对路径 
v1=os.path.dirname(v)  
print(v1)
#还需要往上一级找到项目的路径
v2=os.path.dirname(v1)
sys.path.append(v2)

#上面的代码整合起来如下
import os
import sys
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(_file_)))
sys.path.append(BASE_DIR)
#注意只要sys.path添加项目的路径,那么其项目下的所有py文件(一层)可以直接进行导入,而不需要再导入项目.py文件名

注:模块安装的另外一种方式

通过官网下载对应模块的压缩包,解压,复制解压文件的路径

打开cmd, 使用cd 路径,进入解压文件的路径,dir查看是否有setup文件

再使用 cmd中运行python解释器的代码 setuo.py build

再把build改成install即安装

补充:

"""
#jd.py
n1=666
"""
import jd
print(n1)  #报错:不知道n1是什么,import只是把jd导入

from jd import  jd
print(n1) #666
"""
#jd.py
print(123)
"""
import jd  #123
#import jd #由于已经加载过,就不再加载
#无论是简介还是直接的,只要已经加载过就不再加载

#想要重新加载
import importlib
importlib.reload(jd) #主动重新加载 打印123

print(456) #456
#先打印123再打印456

6.1 random模块

生成随机数

6.1.1 random.randint()

生成一个范围内的随机数

示例:生成随机的验证码

import random

def get_random_code(length=6):
    data=[]
    for i in range(length):
        v=random.randint(65,90)  #生成一个范围内的随机数
        data.append(chr(v))
    return "".join(data)

code =get_random_code()
print(code)

6.1.2 random.choice

在一定的范围内抽取

import random
print(random.choice([1,2,3,4,5]))   #验证码 抽奖 随机在里面抽取一个数字


def identify():
    code=""
    for i in range(4):
        random_num=random.choice([0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"])
        code=code+str(random_num)
    print(code)  #DTKC

identify()

6.1.3 random.sample

与上面的区别是,sample是在范围内抽取,不会出现重复

import random
code=random.sample([0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"],4)
print(code)  #[9, 'u', 0, 'C']

6.1.4 random.uniform

随机在范围内生成小数

import random
# print(random.uniform(1,5))  #1.8075946391409703  取1-5的位数

6.1.5 random.shuffle

洗牌 算法

import random
a=[1,2,3]
obj=random.shuffle(a)
print(obj,a)  #None [3, 1, 2]  也有可能会和原来一样,只是随机打乱顺序

6.2 hashlib模块

摘要算法模块

6.2.1 hashlib.md5()

案例:加密密码

import hashlib

def get_md5(data):
    obj=hashlib.md5()
    obj.update(data.encode("utf-8"))  #字符串是以unicode编码的,需要转换成压缩后的二进制
    result=obj.hexdigest()
    return result

code=input("请输入您的密码:")
result=get_md5(code)
print(result)

#加盐版
def get_md5_new(data):
    obj=hashlib.md5("asdsdhasfjafvajfvha".encode("utf-8"))  #修改的地方在这里
    obj.update(data.encode("utf-8"))
    result=obj.hexdigest()
    return result

print("验证加盐版的加密方法")
result1=get_md5_new(code)
print(result1)

USER_LIST=[]
def register():
    print("****************创建账户****************")
    while True:
        user=input("请输入用户名:")
        if user=="N" or user=="n":
            return
        pwd=input("请输入密码:")
        temp={"username":user,"password":get_md5_new(pwd)}
        USER_LIST.append(temp)

def login():
    print("****************用户登录****************")
    user=input("请输入用户名:")
    pwd=input("请输入密码:")

    for item in USER_LIST:
        if item["username"]==user and item["password"]==get_md5_new(pwd):
            print("登录成功!")
        else:
            print("登录失败!")
register()
print(USER_LIST)

login()

6.2.2 sha

import hashlib
obj=hashlib.sha1("盐".encode())
obj.update(b"str")
print(obj.hexdigest())  #863c5545d295eef3dffe556a141a48b30565c763 40位

sha后面跟的数字越大,其安全性越高,但是一般用sha1就可以

通过加盐之后就可以避免一些破解

6.2.3 密文验证

6.3.4 校验文件的一致性

6.3 getpass

6.3.1 getpass.getpass()

案例:不显示密码(只能在终端上运行)

import getpass

result=getpass.getpass("请输入密码:")   #用户在运行的时候需要在终端(cmd)上才能不显示
if result=="123":
    print("输入正确!")
while True:
    pass

6.4 time & datetime模块

6.4.1 time.time()

时间戳,1970年1月1日0点0分到现在经历的秒数

import time
print(time.time())  #1571888785.1439407

GMT/UTC 世界时间

GMT:格林尼治时间,本初子午线上的地方 划分出来的时间

UTC:精确度更高,和GMT一起称为世界时间

地球上共有24个时区,每个地区所处的时间不一定相同,于是产生了本地时间

在爬虫的时候,可以在微信的网页端看到类似的时间戳,大概是15xx...xx,可能会乘以1000或者其他的数字

如Request URL:

https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=oefyMDcesQ==&tip=1&r=68507023&_=1571889522889

6.4.2 time.slepp()

括号里面填多少,就睡几秒

import time
def func():
    time.sleep(2)
    print(666)

start_time=time.time()  #获取当前时间
func()
end_time=time.time()  #获取当前时间
print(end_time-start_time)

应用:

import time
#装饰器
def wrapper(func):
    def inner(*args,**kwargs):
        start_time = time.time()  # 获取当前时间
        v=func(*args,**kwargs)
        end_time = time.time()  # 获取当前时间
        print(end_time - start_time)
        return v
    return inner

@wrapper
def func1():
    time.sleep(2)
    print(123)

@wrapper
def func2():
    time.sleep(1)
    print(123)

@wrapper
def func3():
    time.sleep(3)
    print(123)

func1()
func2()

6.4.3 time.timezone

和电脑设置的时区相关,查找当前时区与格林尼治时间相隔的秒数

import time
print(time.timezone)  #-28800  当前时区和格林尼治时间所隔的秒数

6.4.4 datetime.datetime

6.4.4.1 datetime.datetime.now

获取本地时间

import datetime
time_obj=datetime.datetime.strptime("2020-1-1 23:32:24","%Y-%m-%d %H:%M:%S") #获取datetime对象
print(time_obj,type(time_obj))  #2020-01-01 23:32:24 <class 'datetime.datetime'>
print(datetime.datetime.now())
6.4.4.2 datetime.datetime.utcnow

显示伦敦时间,即世界时间

6.4.4.3 datetime.datetime.now().strftime

格式化显示时间

from datetime import datetime

#datetime.now() 获取本地时间
ctime=datetime.now()  #年月日时分秒
ctime0=datetime.now().strftime("%Y-%m-%d")  #年月日
ctime1=datetime.now().strftime("%Y-%m-%d-%H-%M-%S")  #年月日时分秒

#datetime.utcnow() 当前utc时间,即世界时间
ctime2=datetime.utcnow()  #当前处于东八区,utc时间比本地时间少8小时

print(ctime,ctime0,ctime1)  #2019-10-24 11:10:02.923210 2019-10-24 2019-10-24-11-10-02

#结合上面的shutil模块应用
#压缩一个文件,按时间命名,并保存再我的码云目录下,最后解压
import shutil
import os
#先判断路径是否存在
if not os.path.exists("我的码云"):
    os.makedirs("我的码云")

#压缩的参数:1.压缩到的路径,2.压缩格式,3.压缩文件
shutil.make_archive(os.path.join("我的码云",ctime),"zip","D:\我的码云")


file_path=os.path.join("我的码云",ctime)+".zip"
#解压缩的参数 1.解压的文件 2.解压到的路径 3.解压的格式
shutil.unpack_archive(file_path,extract_dir=r"D:\x1",format="zip")  

注意:函数返回的都是datetime类型的,不是str类型的

6.4.4.3 datetime.datetime.timedelta
from datetime import datetime
#将datetime格式的数据转换成字符串
v1=datetime.now()
print(v1,type(v1))
val=v1.strftime("%Y-%m-%d %H:%M:%S")  #注意大小写
print(val)

#将字符串转换成datetime
v2=datetime.strptime("2011-11-11","%Y-%m-%d")

#datetime时间的加减
v3=datetime.strptime("2011-11-11","%Y-%m-%d")
v4=v3+timedelta(days=40)  #能加也能减

python中时间日期格式化字符

(1)%y 两位数的年份表示(00-99)

(2)%Y 四位数的年份表示(000-9999)

(3)%m 月份的表示(01=12)

(4)%d 月中的某一天(0-31)

(5)%H 24小时制小时数(0-23)

(6)%I 12小时制小时数(01-12)

(7)%M 分钟数(00-59)

(8)%S 秒数(00-59)

(9)%a 本地简化星期名称

(10)%A 本地完整 星期名称

(11)%b 本地简化月份名称

(12)%B 本地完整月份名称

(13)%c 本地对应的日期表示和时间表示

(14)%j 年内的一天(001-366)

(15)%p 本地A.M.或P.M.的等价 符

(16)%U 一年中的星期数(00-53)星期天为星期的开始

(17)%w 星期(0-6),星期天为星期的开始

(18)%W 一年中的星期数(00-53)星期一位星期的开始

(19)%Z 当前时区的名称

(20)%x本地区相应的日期显示

(21)%X 本地 相应的时间显示 

6.4.4.4 datetime.datetime.fromtimestamp

将时间戳转换为datetime类型

6.4.4.5 datetime.datetime.timestamp

datetime转换为时间戳

import time
from datetime import datetime
#时间戳 和 datetime的关系
ctime=time.time()  #获取时间戳
print(ctime)  #1571973767.657849
v1=datetime.fromtimestamp(ctime)  #将时间戳转换成datetime类型
print(v1)  #2019-10-25 11:22:47.657849

v2=datetime.now()  #获取当前时间
print(v2)
v3=v2.timestamp()  #转换成时间

6.4.6 time.localtime

import time
t=time.localtime()
print(t)
t.tm_hour #不可修改,readonly attrubute
t.tm_mday

6.4.6 time.time

获取时间戳

应用:要计算两个时间的差值,可以将时间转换成时间戳再进行相减,再转换为datetime类型

6.5 sys模块

包含python解释器相关的数据,不推荐去学,了解即可

6.5.1 sys.getrecursionlimit()

获取最大的递归迭代次数,一般电脑设置为1000

6.5.2 sys.getrefcount

引用计数器

import sys
a=[11,22,33]
b=a
print(sys.getrefcount(a))  #3
#在打印的时候也引用了一次

6.5.3 sys.stdout.write

输入输出

sys.stdout.write("你好")
sys.stdout.write("呀")   #相当于print,不会进行换行

补充:

# \n 换行
# \t 制表符 
# \r 回到当前行的起始位置
print("123465\r",end="")
print("你好",end="")

#获取进度条
#在python解释器中可以看出中间过程
import time
for i in range(1,101):
    msg="%s%%\r"%i
    print(msg,end="")
    time.sleep(0.05)
    
#模块
import os
import time
#1.读取文件大小(字节)
file_size=os.stat("python.xmind").st_size
#print(file_size)  #9个字节

#2.一点一点的读取文件
read_size=0  #默认已经读的数据为0字节
with open("python.xmind",mode="rb") as f1,open("a.xmind",mode="wb") as f2:
    while read_size < file_size:
        chunk=f1.read(10)  #每次最多读10个字节
        f2.write(chunk)  #写进文件
        read_size+=len(chunk)
        val = read_size / file_size *100
        print("%s%%\r" %val,end=" ")
        time.sleep(0.05)

6.5.4 sys.argv

获取命令行的参数

#在cmd中运行python程序 在后面添加一个参数
import sys
print(sys.argv[0]) #打印py文件路径
print(sys.argv[1])  #打印出添加的参数

应用:删除文件的脚本

import sys
import shutil

#获取用户执行脚本时,传入的参数。
path=sys.argv
#sys.argv=[D:/我的码云/python/删除文件的脚本.py]
print("删除",path)

if len(path)==1:
    print("未传入参数")
else:
    path=sys.argv[1]
    shutil.rmtree(path)
#应用二:脚本传入两个参数,一个当作路径,一个当内容写入文件
#第一个参数:文件路径
#第二个参数:内容
import sys
if len(sys.agrv)<3:
    print("参数不够,请重新运行")
    #sys.exit(0)
else:
    file_path=sys.argv[1]
    content=sys.argv[2]
    with open(file_path,mode="w",encoding="utf-8") as f:
    	f.write(content)
        

6.5.5 sys.exit()

import sys
def f1():
    pritn("111")

sys.exit(0)  #括号中的内容表示程序正常退出
f1()    #函数不会执行

6.5.6 sys.modules

存储当前程序中用到的所有模块,反射本文件中的内容

6.5.7 sys.path

模块搜索路径 一个模块是否能被导入全看sys.path中是不是有这个模块所在的路径

6.6 os

和操作系统相关的数据

  • os.path.exists(path) #路径如果存在,则返回True,若不存在,则返回False

  • os.stat("aaa.txt").st_size 获取文件的大小

  • os.path.abspath() 获取一个文件的绝对路径

    path="aaa.txt"
    
    import os
    v1=os.path.abspath(path)
    print(v1)  ##D:\我的码云\python\aaa.txt
    #若路径不存在,也会返回一个Path的路径
    
  • os.path.dirname

    import os
    v=r"D:/我的码云/python/day14.py"
    print(os.path.dirname(v))
    

    补充:

    1.为了方便在文件录中不会产生转义字符,一般在字符串前加r

    2.出现在普通字符串中的路径,如果代表的是windows文件路径,则使用 斜杆/ 和 反斜杠\ 是一样的;如果代表的是网络文件路径,则必须使用 斜杆/ ;

  • os.path.join 路径的拼接

    import os
    path="D:\我的码云\python"
    v="day14.py"
    
    result=os.path.join(path,v)
    print(result)  # D:\我的码云\python\day14.py
    result1=os.path.join(result,v)
    print(result1)  #D:\我的码云\python\day14.py\day14.py
    #可以进行多次的拼接
    
  • os.path.getsize

    import os
    print(os.path.getsize("D:\我的码云\python"))  #显示文件大小
    
  • os.path.isfile/os.path.isdir

    import os 
    print(os.path.isfile("D:\我的码云\python"))   #False 判断文件是文件还是文件夹
    print(os.path.iddir("D:\我的码云\python"))   #True
    
  • os.getcwd

    import os 
    print(os.getcwd())  #获取当前文件的上级目录 D:\我的码云\python
    
  • os.listdir 查看一个目录下的所有文件【第一层】

面试题:查看一个文件目录下的所有文件

import os
result=os.listdir(r"D:\我的码云\python")
for path in result:
    print(path)
  • os.walk 查看一个目录下的所有文件【所有层】

    import os
    result=os.walk(r"D:\我的码云\python")
    print(result)  #<generator object walk at 0x00000210F9B6A048>
    #找到路径,但是路径文件一般比较多,会用生成器表示
    for a,b,c in result:
        #a.正在查看的目录
        #b.此目录下的文件夹
        #c.此目录下的文件
        print(a,111,b,111,c)
        for item in c:
            path=os.path.join(a,item)
            print(path)
        break
    
  • os..mkdir() 创建一层的目录,其参数传递的是主目录路径,主目录存在会报错

    #os.mkdir()  创建一层目录下的文件路径
    import os
    file_path=r"db\use.txt"
    os.mkdir("db")   #以上一层目录作为参数,当该层目录已经创建则会报错,且无法创建多层
    with open(file_path,mode="w",encoding="utf-8") as f:
        f.write("asdf")
    
  • os.makedirs() 创建多层下的文件目录,主目录存在也会报错

    #os.makedirs()   创建多层目录下的文件
    import os
    
    file_path=r"db\xx\xo\user.txt"
    
    file_folder=os.path.exists(file_path)  #获取主目录的路径
    if not os.path.exists(file_folder):   #判断主目录 路径是否存在
        os.makedirs(file_folder)
    #os.makedirs(r"db\xx\xo")
    with open(file_path,mode="w",encoding="utf-8") as d:
        d.wirte("sad")
    
  • os.rename() 重命名

    import os
    os.rename("db","sb")
    

6.7 shutil

shutil.make_archive() 压缩文件

shutil.upaack_archive() 解压文件

shutil.rmtree() 删除目录

shutil.move() 重命名 移动文件

import shutil

#删除目录,且没有返回值
#shutil.rmtree("test")

#重命名
#shutil.move("test","good")

#压缩和解压,解压的时候直接解压到目录 ,如果没有则自动创建
#shutil.make_archive("我的码云","zip","D:\我的码云")
shutil.unpack_archive("我的码云.zip",extract_dir="D:\我的码云",format="zip")

6.8 json

json是一种特殊的字符串,通用于大多数的语言,缺点:只能序列化

1.所有的自动串都是双引号

2.最外层只能是列表或者字典

3.只支持int float str list dict bool

4.存在字典的key只能是str

5.不能连续load多次

序列化:将其他数据类型转换为str/bytes

反序列化:把str/bytes转换回去

import json
v=[12,3,4,{"k1":"v1"},True,"asdf"]
#序列化,将python中的值转换为json格式的字符串
v1=json.dumps(v)
print(v1,type(v1))   #[12, 3, 4, {"k1": "v1"}, true, "asdf"]  <class 'str'>
#小结一下规律,双引号变单引号,元组编程列表,布尔值首字母大写变小写

#反序列化,将json格式的字符串转换成python的数据类型
v2='["apple",123]'   #json格式的数据
print(type(v2))  #<class 'str'>
v3=json.loads(v2)
print(type(v3))  #<class 'list'>
Python json
dict object
list,tuple array
str string
int,float number
True true
False false
None null

只支持上面的几种数据类型

#以上的数据类型中有中文,序列化时需要保存中文显示
v={"k1":"v1","k2":"你好"}
import json
val=json.dumps(v)
print(val)  #{"k1": "v1", "k2": "\u4f60\u597d"}
#中文字符会转换成unicode编码的格式
val1=json.dumps(v,ensure_ascii=False)
print(val1)  #{"k1": "v1", "k2": "你好"}  这样可以保留中文的字符

dump和load 需要传入文件的路径,返回的值为None,将序列化写入文件中或者将文件中的json格式数据反序列化出来

import json
v={"k1":"v1","k2":"你好"}
f=open("aaa.txt",mode="w",encoding="utf-8")
val=json.dump(v,f)
f.close()
print(val)

f1=open("aaa.txt",mode="r",encoding="utf-8")
val1=json.load(f1)
f1.close()
print(val1)  #{'k1': 'v1', 'k2': '你好'}

6.9 requests

多用于爬虫

import requests
import json
response=requests.get("https://www/luffycity.com/api/v1/course_sub/category/list/")
print(type(response.text))
data=json.loads(response.text)

6.10 包的相关知识及模块的补充

对于包的定义:

py2:文件中必须要i加上_ init _ .py

py3:不需要,但是建议最好加上

模块的补充:

面试题:列举常用的模块有哪些:json / time / os / sys

  • 什么是模块
    • py文件 写好了的 对程序员直接提供某方面功能的文件
    • import xxx
    • from xxx import xxx
  • 什么是包
    • 文件夹 存储多个py文件的文件夹
    • 如果导入的是一个包的话,这个包里模块默认是不能用的。
    • 导入一个包相当于执行__init__.py文件中的内容,所以为了能使用包的模块一般会在Init.py中导入模块

6.11 pickle

1.几乎所有的数据类型都可以写到文件中

2.支持连续load多次

优点:python中所有的语言都能被他序列化(除了socket对象),缺点:序列化的内容只能python用

import pickle
v={1,2,3,4}
val=pickle.dumps(v)
print(type(val),val)  #<class 'bytes'> b'\x80\x03cbuiltins\nset\nq\x00]q\x01(K\x01K\x02K\x03K\x04e\x85q\x02Rq\x03.'
data=pickle.loads(val)
print(type(data),data) #<class 'set'> {1, 2, 3, 4}


#函数也可以序列化
def f1():
    print(123)

v1=pickle.dumps(f1)
print(type(v1),v1)   #<class 'bytes'> b'\x80\x03c__main__\nf1\nq\x00.'
v2=pickle.loads(v1)
print(type(v2),v2)    #<class 'function'> <function f1 at 0x0000025A29C62378>
v2()    #123

类似于json,pickle也有dump和load

import pickle  
c={111,"five",4}
#f=open("aaa.txt",mode="w",encoding="utf-8")
#val1=pickle.dump(c,f)  #write must be str, not bytes  已经是编码后的
f=open("aaa.txt",mode="wb")
val1=pickle.dump(c,f)
print(val1)  #None
f.close()
l=open("aaa.txt",mode="rb")
val2=pickle.load(l)
print(val2)  #{4, 'five', 111}
l.close()

6.12 logging

  • 记录日志的

  • 用户:流水类的(如银行账号的操作)

  • 程序员:

    • 统计用的
    • 用来做故障的排除 debug
    • 用来记录错误,完成代码的优化
  • basicConfig

    • 使用方便
    • 不能实现编码问题,不能同时向文件和屏幕上输出
    • 实质上是对logging模块的basicConfig做设置
    • logging.debug logging.warning
  • logger对象

    • 复杂

      • 创建一个 logger对象

      • 创建一个文件操作符

      • 创建一个屏幕操作符

      • 创建一个格式

  • 给logger对象绑定文件操作符

    • 给logger对象绑定屏幕操作符

    • 给文件操作符设定格式

    • 给屏幕操作符设定格式

    • 用logger对象来进行操作

      import  logging
      logger=logging.getLogger()  #创建logger对象
      fh=logging.FileHandler("log.log") #创建文件操作符
      sh=logging.StreamHandler() #创建屏幕操作符
      logger.addHandler(fh) #将文件操作符和logger对象绑定到一起
      logger.addHandler(sh) #将屏幕操作符和logger对象绑定到一起
      
      formatter=logging.Formatter("%(asctime)s - %(name)s - %(levelname)s -%(module)s - %(message)s")  #规定格式
      fh.setFormatter(formatter) #文件按规定的格式写
      sh.setFormatter(formatter) #屏幕按规定的格式显示
      
      logger.warning("message") #即打印在屏幕上又输出到文件中
      
import logging
logging.basicConfig(filename="cmdb.log",level=20)
logging.log(10,"aaaaa")
logging.log(30,"ccccc") #30大于level的值20,会写进日志

CRITICAL=50
FATAL=CRITICAL
ERROR=40
WARNING=30
WARN=WARNING
INFO=20
DEBUG=10
NOTSET=0

logging.basicConfig(filename="cmdb.log",level=logging.INFO)

logging.debug("1")
logging.info("2")
logging.warning("3")
logging.error("4")
logging.critical("5")


logging.basicConfig(
    filename="cmdb.log",
    format="%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S %p",
    level=logging.ERROR
)
logging.error("apple")

注意:如果多次使用basicConfig只会写入第一个使用basicConfig的log文件

#日志处理场景应用:对于异常处理捕获到的内容,使用日志模块将其保留到日志文件中
import logging
import requests

logging.basicConfig(
    filename="cmdb.log",
    format="%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S %p",
    level=logging.ERROR
)

try:
    requests.get("http://google.com")
except Exception as e:
    msg=str(e) #调用e.__str__()方法
    logging.error(msg,exc_info=True)  #这样就可以把真正的错误信息发送回去
    #加上exc_info=True可以把堆栈信息也发送

注意:如果想保留异常的堆栈信息,需要加exc_info=True这样的一个参数

日志处理的本质

import logging
file_handler=logging.FileHandler("x1.log","a",encoding="utf-8")  #文件对象
fmt=logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s -%(message)s")
#以后写日志按照上面的格式来进行写 
file_handler.setFormatter(fmt)

logger=logging.Logger("xxxx",level=logging.ERROR) #xxxx表示日志名
logging.addHandler(file_handler) 
logging.error("apple")  #相当于内容写入到文件中

同时写入多个日志文件

import logging
file_handler1=logging.FileHandler("x1.log","a",encoding="utf-8")  #文件对象
fmt1=logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(message)s")
#以后写日志按照上面的格式来进行写 
file_handler1.setFormatter(fmt1)

file_handler2=logging.FileHandler("x2.log","a",encoding="utf-8")  #文件对象
fmt2=logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(message)s")
#以后写日志按照上面的格式来进行写 
file_handler2.setFormatter(fmt2)

logger=logging.Logger("xxxx",level=logging.ERROR) #xxxx表示日志名
logging.addHandler(file_handler1) 
logging.addHandler(file_handler2)
logging.error("apple")  #相当于内容写入到文件中
#一次写入多个日志文件
#推荐日志方式
import  logging
file_handler=logging.FileHandler(filename="x",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,],
    level=logging.ERROR
)

logging.error("apple")
#日志切割
import logging
import time
from logging import handlers
file_handler=handlers.TimedRotatingFileHandler(filename="x3",when="s",interval=5,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],
    level=logging.ERROR
)

for i in range(1,1000):
    time.sleep(1)
    logging.error("apple")

6.13 collections

  • OrderedDict
#有序字典
#创建方法一:
from collections import OrderedDict  #按ctrl点击OrderedDict可以查看其中的方法
info=OrderedDict()
info["k1"]=123
info["k2"]=456
#循环 打印一定先打印k1,后打印k2
for item,key in info.items():
    print(item,key)
    
# k1 123
# k2 456

#创建方法二:
d=dict([('a',1),('b',2),('c',3)])
print(d) #{'a': 1, 'b': 2, 'c': 3}

#创建方法三:
from collections import OrderedDict
odic=OrderedDict([('a',1),('b',2),('c',3)])
print(odic)  #OrderedDict([('a', 1), ('b', 2), ('c', 3)])
for j,k in odic.items():
    print(j,k)
    # a 1
    # b 2
    # c 3
  • defaultdict 默认字典,可以给字典的value 设置一个默认值
  • deque 双端队列
  • namedtuple
#可命名元组
#创建一个类,这个类没有方法,所有属性的值不能修改
from collections import namedtuple
Course=namedtuple("Course",["name","price","teacher"])
python=Course("python","666","apple")
print(python)   #Course(name='python', price='666', teacher='apple')
print(python.name) #python
print(python.price) #666
print(python.teacher) #apple

6.14 copy

6.14.1copy.deepcopy

6.14.2copy.copy

import copy
message=[1,{"usr":{"apple":666},"pwd":888},888]
message1=copy.copy(message)
message2=copy.deepcopy(message)
print(message1,message2)  #[1, {'usr': {'apple': 666}, 'pwd': 888}, 888] [1, {'usr': {'apple': 666}, 'pwd': 888}, 888]
print(id(message),id(message1),id(message2))  #2766335210376 2766335210760 2766335277896
#注意区别
#虽然拷贝后的内容是一样的,但是其中message1只拷贝第一层,message2会拷贝全部
print(id(message[1]),id(message1[1]),id(message2[1]))  #2470338857792 2470338857792 2470368902816
#发现message1其实第二个数据由于有多层,所以只是将其内存地址指向message的第二个数据的内存地址
#故前两个的内存地址相同,而深拷贝则是全部都拷贝一份,其内存地址与原来相比都会不同(例外:小数据池)

6.15 functools

6.15.1 reduce(func,iterable)

6.16 re

regex()

#re模块本身只是用来操作正则表达式的

#和正则本身没关系

#正则表达式是一种规则,匹配字符串的规则

#为什么要有正则表达式

	#匹配字符串
    	#一个人的电话号码
        #一个人的身份证号
        #一个机器的ip地址
    #表单验证
    	#验证用户输入的信息是否准确
        	#11位数字...
        #银行卡
        	#验证可以减少恶意访问系统占用的资源
    #爬虫
    	#从网页源码中获取一些链接、重要数据
#正则规则
	#第一条规则:本身是哪一个字符,就匹配字符串中的哪一个字符串
    #第二条规则:字符组,[字符1字符2] 匹配字符1或字符2,一个字符组就代表匹配一个字,只要这个字符出现在字符组里
    	#范围从小到大 [0-9] [a-z] [A-Z] 根据ascii的顺序从小到大排的
        	#一般情况下不会用[A-z] 这是由于Z的ascii码到a的ascii码之间还有其他符号
            #任意的字母,一般用[a-zA-Z] 这样在字符组间字符1和字符2不做顺序要求,但是在字符1或字符2内部需要
        #[0-9]   等价于 \d
        	#d -> d
            #\d -> \d是转义符 转义符转移了d,让d能够匹配0-9之间的数
        #[]字符组:只要在中括号内的所有字符都是符合规则的字符
        #[^]非字符组:只要在中括号内的所有字符都是不符合规则的字符
    #第三条规则:|表示或,示例为字符1|字符2,如果字符1和2之间有重叠,总是长的在前,短的在后
    	#小数点可以表示任意字符,所以其单纯表示小数点的时候,在其前面加转义符“\”
        #如果没有括号,则或的作用域一般为全部。加上括号可以限定其作用域
        	#www\.(baidu|souhu)\.com   这样就可以匹配两个网址
        
元字符 匹配内容
. 匹配出换行符意外的任意字符
\w 匹配字母或数字或者下划线 (word)
\d 匹配数字 (digit)
\s 匹配任意的空白符 (space)
\n 匹配一个换行符 (next)
\t 匹配一个制表符 (table)
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\w 匹配非字母或者数字或者下划线
\D 表示所有的非数字
\S 表示非空白
\W 表示除了数字、字母、下划线之外的所有字符

匹配所有一切字符

​ [\d\D] [\s\S] [\w\W]

#元字符小结
# \d 所有的数字
# \w 所有的数字、字母、下划线
# \s 空白(空格、换行符、制表符)
# \D 所有的非数字
# \W 除了数字、字母、下划线
# \S 除了空格、换行、制表符
# \t 制表符
# \n 换行符
# .  匹配任意的符号(除了换行)
# [] 匹配括号内的所有字符,且无论括号内有多少字符都只匹配一个字符
	#带有特殊意义的元字符到了字符组内大部分会取消其特殊意义
    #[().+*\]
    #[-] [(-)]   横杠的位置决定其意义,在字符组的开始和结束位置表示其是一个横杠,其他位置表示范围
# [^] 不匹配括号内的字符 
# ^  以...开始
# $  以...结束
# |  或,如果两种情况选一起,有规则重复,那么需要将长的放前面
	 #或者只负责把两个表达式分开,如果是在整个表达式中只对一部分的内容进行或,需要分组
# () 限定或的作用域,或者限定一组量词的作用域,如(\d\w)?

量词:约束元字符的个数

#{n} 表示只能出现n次
#{n,} 表示匹配至少n位
#{n,m} 表示至少出现n次,最多出现m次

#?    表示匹配0次或1次      表示可有可无 但是只能有一个 比如小数点
#+    表示匹配1次或多次     
#*    表示匹配0次或多次     表示可有可无 但是可以有多个 比如小数点后n位数


#匹配任意两位整数
#\d{2}
#匹配任意的有两位小数的数字
#\d+\.\d{2}
#匹配一个整数或者小数
#\d+\.\d+|\d+
#\d+\.?\d*   这样会匹配到12. 这样不规范的数,所以改进位\d+(\.\d+)?  后面的小数点和小数位同时出现或者不出现
#默认贪婪匹配,总是会在符合量词条件的范围额你尽量多匹配
    #\d{7,12}
    #回溯算法
#非贪婪匹配,总是匹配符合条件范围内
    #元字符 量词 ? x   表示按照元字符规则在量词范围内进行匹配,一旦遇到x就结束
    #.*?x 匹配任意的内容任意多次直到遇到x就立即停止
    #?在量词后面表示非贪婪的符号
#练习题
#身份证是一个长度为15或18个字符的字符串,如果是15位则全部有数字组成,首位不为0;
#如果是18位,则前17位全部是数字,首位不为0,末尾可能是数字或者x,下面用正则表达式进行表示:
#[1-9](\d{16}[\dx]|\d{14})
#[1-9](\d{16}(\d|x)|\d{14})
#[1-9]\d{14}(\d{2}[\dx])?

#以a开头,由至少一个字母组成的字符串(不包括a开头)
#a[a-zA-Z]+

#以1开头,中间3-5个数字,如果中间位置超过5个数字,则整个字符串不匹配
#^1\d[3,5]$

6.16.1 findall

import re
ret=re.findall("\d+","apple222")
print(ret)  
#findall会匹配字符串中所有符合规则的项并返回列表,未匹配则返回空列表
import re
ret1=re.search("\d","apple666")
print(ret1)   #_sre.SRE_Match object
#如果能匹配上则返回一个对象,不能匹配则返回None
print(ret1.group()) #6
#会从头到尾去除第一个符合条件的项
#如果是对象,那么这个对象的内部实现了group方法,所以可以取值
#如果是None则这个对象不可能实现了group方法,所以报错,即没有符合的项则会报错
#为了不报错
if ret1:
	print(ret1.group())

6.16.3 match

import re
ret=re.match("\d","apple55")
print(ret) #None
#永远是从头开始匹配,从第一个字符开始是否符合规则
#如果符合,则返回对象,用group取值
#如果不符合,则返回None
#match=search + ^正则

6.16.4 finditer

#降低内存占用率和时间
import re

ret=re.finditer("\d","appppdjaudsygasuytdvuysgaiu123434"*6666666)  #ret是迭代器
for i in ret:   #迭代出来的每一项都是对象
    print(i.group()) #通过group取值即可

6.16.5 compile

import re
ret=re.compile("\d")
print(ret)
r1=ret.search("apple666")
ret.findall("dasyvuysdafu532")

#都利用一样的正则表达式,重复使用多次,能够减少时间的开销
#在查询的结果超过一个的情况下,能够有效的节省内存,降低空间复杂度,从而也降低时间复杂度

6.16.6 split

import re
ret=re.split("\d+","sdasvtyasdf124nufabiu342")  #根据数字切
print(ret)  #["sdasvtyasdf","nufabiu",""]
#ret=re.split("\d+","sdasvtyasdf124nufabiu")  #根据数字切
#print(ret)  #["sdasvtyasdf","nufabiu"]

#默认自动保存分组的内容,需要保存分组的依据,则在正则表达式添加一个括号
ret1=re.split("(\d+)","sdasvtyasdf124nufabiu")  #根据数字切
print(ret1)  #["sdasvtyasdf","123","nufabiu"]

6.16.7 sub

#类似replace
import re 
ret=re.sub("\d","D","sad213dasf")
print(ret)  #sadDDDdasf

ret=re.subn("\d","D","sad213dasf")
print(ret)  #("sadDDDdasf",3)  
#返回一个元组,里面有替换的内容,以及替换的个数

ret=re.sub("\d","D","sad213dasf",1)   #只替换一个
print(ret)  #sadD13dasf

6.16.8 进阶与补充

import re
s1="<h1>wahaha</h1>"
#从其中提取出h1 和 wahaha
res1=re.findall("<(.*?)>",s1)
res2=re.findall("<.*?>(.*?)<.*?>",s1)
print(res1,res2)  #['h1', '/h1'] ['wahaha']
#findall遇到正则表达式,会优先显示分组中的内容

print(re.findall("\d+(\.\d+)?","1.23+5"))  #只想找到表达式中的整数和小数
#但是结果是[".23",""],优先显示分组中的内容,导致只显示小数点后的数字
#为了解决这个问题,取消分组的优先显示
re.findall("\d+(?:\.\d+)?","1.23+5")  #在分组前面加上?:则表示取消分组的优先显示
#["1.23","5"]

#例题:匹配其中的所有小数和整数
#方法一:优先匹配小数,再匹配整数
ret=re.findall(r"\d+\.\d|\d+","1-7+30.4/69*6+(40*4)")
#有时候我们项匹配的内容包含在不想匹配到的呢日哦那个当中,
#这个时候可以利用分组只匹配整数,小数会匹配成空,再去除空,则匹配到所有的整数
ret=re.findall(r"\d+\.\d|(\d+)","1-7+30.4/69*6+(40*4)")
ret.remove("") 

ret=re.search("<(\w+)>(.*?)<(/\w+)>",s1)
print(ret)  #<re.Match object; span=(0, 15), match='<h1>wahaha</h1>'>
print(ret.group())  #<h1>wahaha</h1>   默认整个表达式分成一组,其默认参数为0,即与ret.group(0)结果相同
print(ret.group(1),ret.group(2),ret.group(3))  #h1 wahaha /h1  分别取第一个、第二个、第三个分组中的内容

#给分组取名字,并可以用group("名字")找到其分组的内容
#(?P<名字>正则表达式) 尽量取名贴合需要匹配的正则表达式,且不能取相同的名字,会报错
result=re.search("<(?P<tag>.*?)>(?P<name>.*?)<.*?>",s1)
print(result.group("name"))  #wahaha
print(result.group("tag"))   #h1

#为了确定<h1> 对应到后面的</h1>
#可以利用分组取名的方法,引用分组(?P=name)
#这个组中的内容必须完全和原来已经存在的分组匹配到的内容一致
re3=re.search("<(?P<tag>\w+)>.*?</(?P=tag)>",s1)
print(re3.group(1))  #h1  严格约束前面的内容与后面的内容相对应
#简化
re4=re.search(r"<(\w+)>.*?</\1>",s1)  #引用第一个分组的内容
print(re4.group(1))  #h1
#注意:\1在python中表示空格,要在正则表达式中正确使用,需要加上转义r
posted @ 2020-11-02 17:45  wrrr  阅读(57)  评论(0编辑  收藏  举报