Python模块总结

Python模块总结

一、Python模块定义与分类

1.1、定义

把一些常用的函数放在一个py文件中,这个文件就称之为模块,模块,就是一些列常用功能的集合体

1.2、为什么使用模块

1)通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用

2)拿来主义,提升开发效率

1.3、模块分类

内置模块:也叫做标准库。此类模块就是python解释器给你提供的,比如我们之前见过的time模块,os模块。标准库的模块非常多(200多个,每个模块又有很多功能)

第三方模块:一些python大神写的非常好用的模块,必须通过pip install 指令安装的模块,比如BeautfulSoup, Django等。大概有6000多个

自定义模块:自己在项目中定义的一些模块

二、自定义模块

2.0、准备模块

# 文件名: abcd.py

print('from the abcd.py')

def read1():
    print('abcd模块:', name)

def read2():
    print('abcd模块')
    read1()

def change():
    global name
    name = 'barry'

2.1、import使用

当我引用abcd模块的时候,实际上将abcd.py执行一遍,加载到内存.

import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句

import abcd
import abcd
import abcd
import abcd
import abcd
'''
from the abcd.py
'''

2.1.1、第一次导入模块执行三件事

1)创建一个以模块名命名的名称空间。

2)执行这个名称空间(即导入的模块)里面的代码。

3)通过此模块名. 的方式引用该模块里面的内容(变量,函数名,类名等)

ps:重复导入会直接引用内存中已经加载好的结果

2.1.2、被导入模块有独立的名称空间

每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

2.1.3、模块起别名

1)可以将很长的模块名改成很短,方便使用

import abcd as tb
print(tb.name)

2)有利于代码的扩展和优化

# mysql_.py
def sqlprase():
    print('连接mysql数据库')
# oracle_.py
def sqlprase():
    print('连接oracle数据库')
content = input('>>>').strip()
if content == 'mysql':
    import mysql_
    mysql_.sqlprase()
elif content == 'oracle':
    import oracle_
    oracle_.sqlprase()

# -------------------------------
# 统一化接口
content = input('>>>').strip()

if content == 'mysql':
    import mysql_ as db
elif content == 'oracle':
    import oracle_ as db

db.sqlprase()

2.1.4、导入多个模块

# 引入多个模块
import time,os,sys  # 不推荐.

# 推荐:多行导入:易于阅读 易于编辑 易于搜索 易于维护
import time
import os
import sys

2.2、from ... import ...

2.2.1、from ... import ... 使用

from abcd import name
from abcd import read1

# 相当于从abcd模块的全局空间中将name,read1变量与值的对应关系复制到当前执行文件的全局名称空间中.
print(globals())

print(name)  # 运维人在路上
read1()  # abcd模块: 运维人在路上

# 优点:使用起来方便了.
# 缺点:容易与当前执行文件产生覆盖效果.

2.2.2、from...import... 与import对比

使用from...import...则是将模块中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀

好处:使用方便

缺点:执行文件有与模块同名的变量或者函数名,会有覆盖效果

2.2.3、一行导入多个

from bacd import read1,read2,name

2.2.4、from ... import *

from 模块 import * :把模块中所有的不是以下划线(_)开头的名字都导入到当前位置,不推荐使用这种导入方式

# from ... import *  尽量别单独用
from abcd import *
print(name)
read1()
read2()
# 1、全部将abcd的所有名字复制过来,无用功.
# 2、容易覆盖.
# 3、from ... import * 与__all__配合使用(写在模块文件中)

三、内置模块

3.1、序列化模块

序列化:将一个常见的数据结构转化成一个特殊的序列,并且这个特殊的序列还可以反解回去。

主要用途:文件读写数据,网络传输数据

3.1.1、json模块

json序列化只支持部分Python数据结构:dict,list, tuple,str,int, float,True,False,None

json模块是将满足条件的数据结构转化成特殊的字符串,并且也可以反序列化还原回去。

json主要有两对4个方法:

  • 用于网络传输:dumps、loads
  • 用于文件写读:dump、load

1)dumps、loads

# 序列化:将一个字典转换成一个字符串
str_dic = json.dumps(dic)
# 注意,json转换完的字符串类型的字典中的字符串是由""表示的
print(type(str_dic), str_dic)  # <class 'str'> {"k1": "v1", "k2": "v2", "k3": "v3"}


# 反序列化:将一个字符串格式的字典转换成一个字典
dic2 = json.loads(str_dic)
# 注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2), dic2)  # <class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

# 也可以处理嵌套的数据类型
list_dic = [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
str_dic = json.dumps(list_dic)
print(type(str_dic), str_dic)  # <class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2), list_dic2)  # <class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]

特殊参数:

dic = {'username': '运维人在路上', 'password': 123, 'status': False}
print(dic)  
ret = json.dumps(dic)
print(ret, type(ret))   # {"username": "u8fd0u7ef4u4ebau5728u8defu4e0a", "password": 123, "status": false} <class 'str'>
ret = json.dumps(dic, ensure_ascii=False, sort_keys=True)
print(ret, type(ret))   # {"password": 123, "status": false, "username": "运维人在路上"} <class 'str'>

# -----------------------------------------------------
ensure_ascii:当它为True的时候,所有非ASCII码字符显示为uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示
separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。 
sort_keys:将数据根据keys的值进行排序

​ 需求:将字典写入文件并读出

import json

dic = {'username': '运维人在路上', 'password': 123, 'status': False}
s_dict = json.dumps(dic)
with open('jsonlx.json', encoding='utf-8', mode='w') as f1:
    f1.write(s_dict)

with open('jsonlx.json', encoding='utf-8') as f2:
    content = f2.read()
    print(json.loads(content))

2)dump、load

​ 单个数据的存取文件

import json

dic = {'username': '运维人在路上', 'password': 123, 'status': False}
with open('jsonlx1.json', encoding='utf-8', mode='w') as f1:
    json.dump(dic, f1)

with open('jsonlx1.json', encoding='utf-8') as f1:
    dic1 = json.load(f1)

print(dic1, type(dic1))

3.1.2、pickle模块

pickle模块是将Python所有的数据结构以及对象等转化成bytes类型,然后还可以反序列化还原回去

使用上与json几乎差不多,也是两对四个方法:

  • 用于网络传输:dumps、loads
  • 用于文件写读:dump、load

1)dumps、loads

import pickle

dic = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  # bytes类型  

dic2 = pickle.loads(str_dic)
print(dic2)  # 字典 {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

# ------------------------------------------------------------
# 还可以序列化对象
def func():
    print(666)


ret = pickle.dumps(func)
print(ret, type(ret))  # b'x80x03c__main__
func
qx00.' <class 'bytes'>
f1 = pickle.loads(ret)  # f1得到 func函数的内存地址
f1()  # 执行func函数

2)dump、load

# dump load 数据结构存取文件.
import pickle

l1 = ['wusir', '太白', '小黑', 666]
with open('pickle练习.pickle', mode='wb') as f1:
    pickle.dump(l1, f1)

with open('pickle练习.pickle', mode='rb') as f1:
    ret = pickle.load(f1)
    print(ret, type(ret))
# 多个数据写入文件
l1 = ['wusir', '太白', '小黑1', 666]
l2 = ['wusir', '太白', '小黑2', 666]
l3 = ['wusir', '太白', '小黑3', 666]
with open('pickle1.pickle', mode='wb') as f1:
    pickle.dump(l1, f1)
    pickle.dump(l2, f1)
    pickle.dump(l3, f1)

with open('pickle1.pickle', mode='rb') as f1:
    ret1 = pickle.load(f1)
    ret2 = pickle.load(f1)
    ret3 = pickle.load(f1)
    print(ret1, ret2, ret3)

3.2、os模块

import os

# 与路径相关
print(os.getcwd())  # 绝对路径
os.chdir(r'D:python学习')
print(os.getcwd())  # D:python学习
print(os.curdir)  # .
print(os.pardir)  # ..
print("=============================")

# 和文件夹相关
os.makedirs('dirname1/dirname2/dirname3/dirname4')  # 多级目录
os.removedirs('dirname1/dirname2/dirname3/dirname4')  # 删除截止到有文件的那层
os.mkdir(r'd:abc')  # 单级目录
os.rmdir(r'd:abc')
print(os.listdir(r'D:python学习'))

# 文件相关
os.remove()  # 删除一个文件
os.rename("oldname", "newname")  # 重命名文件/目录
print(os.stat(r'D:python学习os模块.py'))

# path 和路径相关
print(os.path.abspath('04 os模块.py'))  # D:s23day174 os模块.py
print(os.path.split(os.path.abspath('01 昨日内容回顾.py')))  # ('D:\s23\day17', '01 昨日内容回顾.py')
print(os.path.dirname(r'D:s23day91 初始函数.py'))  # 获取父级目录
print(os.path.dirname(os.path.abspath('01 昨日内容回顾.py')))
print(__file__)  # 动态获取当前文件的绝对路径

# 获取当前文件的爷爷级的目录
print(os.path.dirname(os.path.dirname(__file__)))
print(os.path.basename(r'D:s23day91 初始函数.py'))  # 获取文件名
print(os.path.exists(r'D:s23day92 初始函数.py'))

# 判断是否是绝对路径
print(os.path.isabs(r'D:s23day91 初始函数.py'))
print(os.path.isabs(r'day17/01 昨日内容回顾.py'))

# 判断该路径是否是一个文件路径
print(os.path.isfile(r'D:s23day91 初始函数.py'))
print(os.path.isfile(r'D:s23day9'))
print(os.path.isdir(r'D:s23day17dirname1dirname2'))
print(os.path.exists(r'D:s23day17dirname1dirname2'))

# 判断是否是一个目录(文件夹)
print(os.path.isdir(r'D:s23day172 序列化模块.py'))
# D:s23day16评论文章
path = os.path.join('D:','s23','day20','随便')
print(path)

# ======================================================
当前执行这个python文件的工作目录相关的工作路径
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下为"	
",Linux下为"
" *
# os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为: *
# os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix' *
# 和执行系统命令相关
# os.system("bash command")  运行shell命令,直接显示  **
# os.popen("bash command).read()  运行shell命令,获取执行结果  **
os.environ  获取系统环境变量  **
​
#path系列,和路径相关
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的大小 ***

注意:os.stat('path/filename') 获取文件/目录信息 的结构说明

# stat 结构:
st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间)

3.3、sys模块

sys.argv  		# 命令行参数List,第一个元素是程序本身路径
sys.exit()  	# 退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version  	# 获取Python解释程序的版本信息
sys.path  		# 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值  ***
sys.platform  	# 返回操作系统平台名称,不准

3.4、hashlib模块

此模块称为摘要算法,也叫做加密算法,或者是哈希算法,散列算法

通过一个函数,把任意长度的数据按照一定规则转换为一个固定长度的数据串(通常用16进制的字符串表示),用于加密解密

hashlib的特征:

  1. bytes类型数据>通过hashlib算法>固定长度的字符串
  2. 不同的bytes类型数据转化成的结果一定不同。
  3. 相同的bytes类型数据转化成的结果一定相同。
  4. 此转化过程不可逆。

hashlib主要用途:

  • 密码加密
  • 文件一致性校验

3.4.1、密码加密

# md5加密
ret = hashlib.md5()
ret.update('123'.encode('utf-8'))
s = ret.hexdigest()
print(s, type(s))   # 202cb962ac59075b964b07152d234b70 <class 'str'>

ret = hashlib.md5()
ret.update('123'.encode('utf-8'))
s = ret.hexdigest()
print(s, type(s))   # 202cb962ac59075b964b07152d234b70 <class 'str'>

ret = hashlib.md5()
ret.update('223'.encode('utf-8'))
s = ret.hexdigest()
print(s, type(s))   # 115f89503138416a242f40fb7d7f338e <class 'str'>

ret = hashlib.md5()
ret.update('22fdslkafjdsklfdsjafldsjf3'.encode('utf-8'))
s = ret.hexdigest()
print(s, type(s))   # 3ebcde7d2fc16401c8b42a7994ca34d4 <class 'str'>

3.4.2、固定盐加密

# 加固定盐
ret = hashlib.md5('abcf'.encode('utf-8'))	# 盐:abcf
ret.update('123456'.encode('utf-8'))
s = ret.hexdigest()
print(s, type(s))   # d8128a28e5f55017ebab945a6b80db0d <class 'str'>

3.4.3、动态盐加密

# 加动态的盐
username = input('输入用户名:').strip()
password = input('输入密码').strip()
ret = hashlib.md5(username[::2].encode('utf-8'))    # 针对于每个账户,每个账户的盐都不一样
ret.update(password.encode('utf-8'))
s = ret.hexdigest()
print(s)

3.4.4、其他加密算法

对安全要求比较高的企业,比如金融行业,MD5加密的方式就不够了,得需要加密方式更高的,比如sha系列,sha1,sha224,sha512等等,数字越大,加密的方法越复杂,安全性越高,但是效率就会越慢

ret = hashlib.sha1()
ret.update('yunweiren'.encode('utf-8'))
print(ret.hexdigest())

# 也可加盐
ret = hashlib.sha384(b'asfdsa')
ret.update('yunweiren'.encode('utf-8'))
print(ret.hexdigest())

# 也可以加动态的盐
ret = hashlib.sha384(b'asfdsa'[::2])
ret.update('yunweiren'.encode('utf-8'))
print(ret.hexdigest())

3.4.5、文件一致性校验

md5计算的就是bytes类型的数据的转换值,同一个bytes数据用同样的加密方式转化成的结果一定相同,如果不同的bytes数据(即使一个数据只是删除了一个空格)那么用同样的加密方式转化成的结果一定是不同的。所以,hashlib也是验证文件一致性的重要工具

# 分段读,避免撑爆内存
import hashlib

def md5_file(path):
    ret = hashlib.md5()
    with open(path, mode='rb') as f1:
        while 1:
            content = f1.read(1024)
            if content:
                ret.update(content)
            else:
                return ret.hexdigest()

print(md5_file(r'python-3.7.4rc1-embed-win32.zip'))

3.5、time模块

表示时间的三种方式

1)时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量

2)格式化的时间字符串(Format String): '1999-12-06'

%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)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)

image-20210308111501234

import time

print(time.time())  # 1615173465.8671505

# 格式化时间:
# 字符串类型
print(time.strftime("%Y-%m-%d %H:%M:%S"))  # 2021-03-08 11:17:45
print(time.strftime("%y-%m-%d %H:%M:%S  %A"))  # 21-03-08 11:17:45  Monday
ret = time.strftime("%Y{}%m{}%d{} %H:%M:%S")
print(ret.format('年', '月', '日'))  # 2021年03月08日 11:17:45

# 获取结构化时间
print(time.localtime())  # time.struct_time(tm_year=2021, tm_mon=3, tm_mday=8, tm_hour=11, tm_min=19, tm_sec=9, tm_wday=0, tm_yday=67, tm_isdst=0)

# ------------------------------------------------------------
# 时间戳 转化成 格式化时间
timestamp = time.time()
st = time.localtime(timestamp)
ft = time.strftime("%Y-%m-%d %H:%M:%S", st)
print(ft)   # 2021-03-08 11:20:56

# 格式化时间转化成时间戳
ft = time.strftime("%y-%m-%d %H:%M:%S")
print(ft)   # 21-03-08 11:24:16
st = time.strptime(ft, "%y-%m-%d %H:%M:%S")
print(st)   # time.struct_time(tm_year=2021, tm_mon=3, tm_mday=8, tm_hour=11, tm_min=24, tm_sec=16, tm_wday=0, tm_yday=67, tm_isdst=-1)
timestamp = time.mktime(st)
print(timestamp)

几种格式之间的转换

image

3.6、datatime模块

# datatime模块
import datetime
now_time = datetime.datetime.now()  # 现在的时间
# 只能调整的字段:weeks days hours minutes seconds
print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后
print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三周前
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后
print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后
print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前
print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前
print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后
print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前
print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后

current_time = datetime.datetime.now()
# 可直接调整到指定的 年 月 日 时 分 秒 等

print(current_time.replace(year=1977))  # 直接调整到1977年
print(current_time.replace(month=1))  # 直接调整到1月份
print(current_time.replace(year=1989,month=4,day=25))  # 1989-04-25 18:49:05.898601

# 将时间戳转化成时间
print(datetime.date.fromtimestamp(1232132131))  # 2009-01-17

3.7、random模块

import random

print(random.random())  # 大于0且小于1之间的小数
print(random.uniform(1, 6))  # 大于1且小于6之间的小数

print(random.randint(1, 5))  # 大于等于1且小于5(5可以取到)
print(random.randrange(1, 10, 2))  # 大于等于1且小于10之间的奇数

print(random.choice(['如花', '凤姐', '石榴姐', 1]))
print(random.sample(('如花', '凤姐', '石榴姐'), 2))  # 可以控制元素个数  ***

# 打乱顺序
item = [i for i in range(10)]
random.shuffle(item)
print(item) # [6, 0, 9, 2, 3, 8, 7, 1, 4, 5]

需求:生成随机验证码

import random

def code(amount):
    str_code = ''
    for i in range(amount):
        num = random.randint(0, 9)  # 6
        lower_char = chr(random.randint(97, 122))  # y
        upper_char = chr(random.randint(65, 90))  # A
        single_char = random.choice([num, lower_char, upper_char])
        str_code += str(single_char)
    return str_code

print(code(4))
print(code(5))

3.8、logging模块

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING

日志级别等级:CRITICAL > ERROR > WARNING > INFO > DEBUG

1)第一版日志配置

import logging
logging.basicConfig(
    level=logging.DEBUG,
    # level=30,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    filename=r'test.log',
)
logging.debug('调试模式')  # 10
logging.info('正常模式')  # 20
logging.warning('警告信息')  # 30
logging.error('错误信息')  # 40
logging.critical('严重错误信息')  # 50

参数说明

# 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用户输出的消息

缺点: 文件与屏幕输入只能选择一个

2)第二版日志配置:对象方式

import logging

# 创建一个logging对象
logger = logging.getLogger()

# 创建一个文件对象
fh = logging.FileHandler('rc.log', encoding='utf-8')

# 创建一个屏幕对象
sh = logging.StreamHandler()

# 配置显示格式
formatter1 = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
formatter2 = logging.Formatter('%(asctime)s %(message)s')
fh.setFormatter(formatter1)
sh.setFormatter(formatter2)

logger.addHandler(fh)
logger.addHandler(sh)

# 总开关
logger.setLevel(10)
# 分别设置日志级别
fh.setLevel(10)
sh.setLevel(40)

logging.debug('调试模式')  # 10
logging.info('正常模式')  # 20
logging.warning('警告信息')  # 30
logging.error('错误信息')  # 40
logging.critical('严重错误信息')  # 50

logging库提供了多个组件:Logger、Handler、Filter、Formatter

  • Logger对象:提供应用程序可直接使用的接口
  • Handler:发送日志到适当的目的地
  • Filter:提供了过滤日志信息的方法
  • Formatter:指定日志显示格式。

可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过fh.setLevel(logging.Debug)单对文件流设置某个级别

3)第三版日志配置:通过配置文件方式

import os
import logging.config

# 定义三种日志输出格式
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'

# 定义日志输出格式
logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
logfile_name = 'all2.log'  # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)

# 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': logfile_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)传递
        },
    },
}


def load_my_logging_cfg(task_id):
    logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
    logger = logging.getLogger(task_id)  # 生成一个log实例
    return logger

def login():
    logger1 = load_my_logging_cfg('登录部分')
    logger1.info('xx人登陆成功')

def transfer():
    logger2 = load_my_logging_cfg('转账部分')
    logger2.info('张三给李四转账成功!')

3.9、collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等

  • namedtuple: 生成可以使用名字来访问元素内容的tuple
  • deque: 双端队列,可以快速的从另外一侧追加和推出对象
  • Counter: 计数器,主要用来计数
  • OrderedDict: 有序字典
  • defaultdict: 带有默认值的字典

3.9.1、namedtuple:命名元祖

from collections import namedtuple

# 格式
# namedtuple('名称', [属性list])

# 表示一个点
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(type(p))  # <class '__main__.Point'>
print(p)  # Point(x=1, y=2)
print(p[0])  # 1
print(p[1])  # 2
print(p.x)  # 1
print(p.y)  # 2

# 表示圆
Circle = namedtuple('Circle', ['x', 'y', 'r'])
c = Circle(1, 2, 1)
print(type(c))  # <class '__main__.Circle'>

# -------------------------------------------
from collections import namedtuple

struct_time = namedtuple('struct_time', ['tm_year', 'tm_mon', 'tm_mday'])
st = struct_time(2019, 7, 2)
print(st)  # struct_time(tm_year=2019, tm_mon=7, tm_mday=2)

3.9.2、deque:双端队列

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低

deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈

# deque: 类似于列表的一种容器型数据,插入元素删除元素效率高.
from collections import deque

q = deque(['a', 1, 'c', 'd'])
print(q)  # deque(['a', 1, 'c', 'd'])
q.append('e')
print(q)  # deque(['a', 1, 'c', 'd', 'e'])
q.appendleft('ly')
print(q)  # deque(['ly', 'a', 1, 'c', 'd', 'e'])

# 删除
q.pop()
q.popleft()
print(q)  # deque(['a', 1, 'c', 'd'])

# 按照索引取值
print(q[0])  # a

# 按照索引删除任意值
del q[2]
print(q)    # deque(['a', 1, 'd'])
q.insert(1, '2')
print(q)    # deque(['a', '2', 1, 'd'])

3.9.3、OrderedDict:有序字典

注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序

d = dict([('a', 1), ('b', 2), ('c', 3)])
print(d)

from collections import OrderedDict

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od)
print(od['a'])
print(od['b'])

# ---------------------------------
from collections import OrderedDict

od = OrderedDict()
od['z'] = "Z"
od['x'] = "X"
od['y'] = "Y"
print(od)   # OrderedDict([('z', 'Z'), ('x', 'X'), ('y', 'Y')])

3.9.4、defaultdict:默认字典

使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict

from collections import defaultdict

l1 = [11, 22, 33, 44, 55, 77, 88, 99]
dic = {}
for i in l1:
    if i < 66:
        if 'key1' not in dic:
            dic['key1'] = []
        dic['key1'].append(i)

    else:
        if 'key2' not in dic:
            dic['key2'] = []
        dic['key2'].append(i)
print(dic)  # {'key1': [11, 22, 33, 44, 55], 'key2': [77, 88, 99]}

# ----------------------------------------------
l1 = [11, 22, 33, 44, 55, 77, 88, 99]
dic = defaultdict(list)
for i in l1:
    if i < 66:
        dic['key1'].append(i)

    else:
        dic['key2'].append(i)
print(dic)	# defaultdict(<class 'list'>, {'key1': [11, 22, 33, 44, 55], 'key2': [77, 88, 99]})

# ----------------------------------------------
# 需要一个可回调的
dic = defaultdict(lambda :None)
# dic = defaultdict(None)   # 报错
for i in range(1,4):
    dic[i]
print(dic)

3.9.5、counter:计数器

c = Counter('flkjdasffdfakjsfdsaklfdsalf')  # 计数器
print(c)    # Counter({'f': 7, 'd': 4, 'a': 4, 's': 4, 'l': 3, 'k': 3, 'j': 2})
print(c['f'])   # 7

3.10、re模块

正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法

元字符 匹配内容
w 匹配字母(包含中文)或数字或下划线
W 匹配非字母(包含中文)或数字或下划线
s 匹配任意的空白符
S 匹配任意非空白符
d 匹配数字
D p匹配非数字
A 从字符串开头匹配
z 匹配字符串的结束,如果是换行,只匹配到换行前的结果
匹配一个换行符
匹配一个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中的字符的所有字符
* 匹配0个或者多个左边的字符。
+ 匹配一个或者多个左边的字符。
匹配0个或者1个左边的字符,非贪婪方式。
{n} 精准匹配n个前面的表达式。
{n,m} 匹配n到m次由前面的正则表达式定义的片段,贪婪方式
a|b 匹配a或者b。
() 匹配括号内的表达式,也表示一个组

3.10.1、匹配模式举例

# 1,之前学过的字符串的常用操作:一对一匹配
s1 = 'fdskahf太白金星'
print(s1.find('太白'))  # 7

# 2,正则匹配:
# 单个字符匹配
import re

# w 与 W
print(re.findall('w', '太白jx 12*() _'))  # ['太', '白', 'j', 'x', '1', '2', '_']
print(re.findall('W', '太白jx 12*() _'))  # [' ', '*', '(', ')', ' ']

# s 与S
print(re.findall('s', '太白barry*(_ 	 
'))  # [' ', '	', ' ', '
']
print(re.findall('S', '太白barry*(_ 	 
'))  # ['太', '白', 'b', 'a', 'r', 'r', 'y', '*', '(', '_']

# d 与 D
print(re.findall('d', '1234567890 alex *(_'))  # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
print(re.findall('D', '1234567890 alex *(_'))  # [' ', 'a', 'l', 'e', 'x', ' ', '*', '(', '_']

# A 与 ^
print(re.findall('Ahel', 'hello 太白金星 -_- 666'))  # ['hel']
print(re.findall('^hel', 'hello 太白金星 -_- 666'))  # ['hel']

# 、z 与 $  @@
print(re.findall('666', 'hello 太白金星 *-_-* 
666'))  # ['666']
# print(re.findall('666z','hello 太白金星 *-_-* 
666'))  # [] # python3.7报错
print(re.findall('666$', 'hello 太白金星 *-_-* 
666'))  # ['666']

# 
 与 	
print(re.findall('
', 'hello 
 太白金星 	*-_-*	 
666'))  # ['
', '
']
print(re.findall('	', 'hello 
 太白金星 	*-_-*	 
666'))  # ['	', '	']

# 重复匹配
# . ? * + {m,n} .* .*?
# . 匹配任意字符,除了换行符(re.DOTALL 这个参数可以匹配
)。
print(re.findall('a.b', 'ab aab a*b a2b a牛b a
b'))  # ['aab', 'a*b', 'a2b', 'a牛b']
print(re.findall('a.b', 'ab aab a*b a2b a牛b a
b', re.DOTALL))  # ['aab', 'a*b', 'a2b', 'a牛b']

# ?匹配0个或者1个由左边字符定义的片段。
print(re.findall('a?b', 'ab aab abb aaaab a牛b aba**b'))  # ['ab', 'ab', 'ab', 'b', 'ab', 'b', 'ab', 'b']

# * 匹配0个或者多个左边字符表达式。 满足贪婪匹配 @@
print(re.findall('a*b', 'ab aab aaab abbb'))  # ['ab', 'aab', 'aaab', 'ab', 'b', 'b']
print(re.findall('ab*', 'ab aab aaab abbbbb'))  # ['ab', 'a', 'ab', 'a', 'a', 'ab', 'abbbbb']

# + 匹配1个或者多个左边字符表达式。 满足贪婪匹配  @@
print(re.findall('a+b', 'ab aab aaab abbb'))  # ['ab', 'aab', 'aaab', 'ab']

# {m,n}  匹配m个至n个左边字符表达式。 满足贪婪匹配  @@
print(re.findall('a{2,4}b', 'ab aab aaab aaaaabb'))  # ['aab', 'aaab']

# .* 贪婪匹配 从头到尾.
print(re.findall('a.*b', 'ab aab a*()b'))  # ['ab aab a*()b']

# .*? 此时的?不是对左边的字符进行0次或者1次的匹配,
# 而只是针对.*这种贪婪匹配的模式进行一种限定:告知他要遵从非贪婪匹配 推荐使用!
print(re.findall('a.*?b', 'ab a1b a*()b, aaaaaab'))  # ['ab', 'a1b', 'a*()b']

# []: 括号中可以放任意一个字符,一个中括号代表一个字符
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
# ^ 在[]中表示取反的意思.
print(re.findall('a.b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b', 'a4b', 'a*b', 'arb', 'a_b']
print(re.findall('a[abc]b', 'aab abb acb adb afb a_b'))  # ['aab', 'abb', 'acb']
print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b']
print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b'))  # ['aeb', 'arb']
print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b'))  # ['aAb', 'aWb', 'aeb', 'arb']
print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b'))  # ['a11b', 'a12b', 'a34b']
print(re.findall('a[*-+]b', 'a-b a*b a+b a/b a6b'))  # ['a*b', 'a+b']
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
print(re.findall('a[-*+]b', 'a-b a*b a+b a/b a6b'))  # ['a-b', 'a*b', 'a+b']
print(re.findall('a[^a-z]b', 'acb adb a3b a*b'))  # ['a3b', 'a*b']

# 练习:
# 找到字符串中'alex_sb ale123_sb wu12sir_sb wusir_sb ritian_sb' 的 alex wusir ritian
print(re.findall('([a-z]+)_sb', 'alex_sb ale123_sb wusir12_sb wusir_sb ritian_sb'))

# 分组:
# () 制定一个规则,将满足规则的结果匹配出来
print(re.findall('(.*?)_sb', 'alex_sb wusir_sb 日天_sb'))  # ['alex', ' wusir', ' 日天']

# 应用举例:
print(re.findall('href="(.*?)"', '<a href="http://www.baidu.com">点击</a>'))  # ['http://www.baidu.com']

# | 匹配 左边或者右边
print(re.findall('alex|太白|wusir', 'alex太白wusiraleeeex太太白odlb'))  # ['alex', '太白', 'wusir', '太白']
print(re.findall('compan(y|ies)',
                 'Too many companies have gone bankrupt, and the next one is my company'))  # ['ies', 'y']
print(re.findall('compan(?:y|ies)',
                 'Too many companies have gone bankrupt, and the next one is my company'))  # ['companies', 'company']
# 分组() 中加入?: 表示将整体匹配出来而不只是()里面的内容。

3.10.2、常用方法举例

import re

# 1 findall 全部找到返回一个列表。
print(re.findall('a', 'alexwusirbarryeval'))  # ['a', 'a', 'a']

# 2 search 只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
print(re.search('sb|alex', 'alex sb sb barry 日天'))  # <_sre.SRE_Match object; span=(0, 4), match='alex'>
print(re.search('alex', 'alex sb sb barry 日天').group())  # alex

# 3 match:同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
print(re.match('barry', 'barry alex wusir 日天'))  # <_sre.SRE_Match object; span=(0, 5), match='barry'>
print(re.match('barry', 'barry alex wusir 日天').group())  # barry

# 4 split 分割 可按照任意分割符进行分割
print(re.split('[ ::,;;,]', 'alex wusir,日天,太白;女神;肖锋:吴超'))  # ['alex', 'wusir', '日天', '太白', '女神', '肖锋', '吴超']

# 5 sub 替换
print(re.sub('barry', '太白', 'barry是最好的讲师,barry就是一个普通老师,请不要将barry当男神对待。'))
print(re.sub('barry', '太白', 'barry是最好的讲师,barry就是一个普通老师,请不要将barry当男神对待。', 2))    # 替换两个
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)', r'52341', r'alex is sb'))

# 6 compile
obj = re.compile('d{2}')
print(obj.search('abc123eeee').group())  # 12
print(obj.findall('abc123eeee'))  # ['12'],重用了obj

# import re
ret = re.finditer('d', 'ds3sy4784a')  # finditer返回一个存放匹配结果的迭代器
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  # 3 查看第一个结果
print(next(ret).group())  # 4 查看第二个结果
print([i.group() for i in ret])  # 查看剩余的左右结果['7', '8', '4']

3.10.3、命名分组举例

import re

# 命名分组匹配:
ret = re.search("<(?P<tag_name>w+)>w+</(?P=tag_name)>", "<h1>hello</h1>")
# 还可以在分组中利用?<name>的形式给分组起名字,获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name'))  # 结果 :h1
print(ret.group())  # 结果 :<h1>hello</h1>

ret = re.search(r"<(w+)>w+</1>", "<h1>hello</h1>")
# 如果不给组起名字,也可以用序号来找到对应的组,表示要找的内容和前面的组内容一致,获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  # 结果 :<h1>hello</h1>

3.10.4、正则匹配练习

import re

# 1,"1-2*(60+(-40.35/5)-(-4*3))"
# 1.1 匹配所有的整数
print(re.findall('d+', "1-2*(60+(-40.35/5)-(-4*3))"))  # ['1', '2', '60', '40', '35', '5', '4', '3']

# 1.2 匹配所有的数字(包含小数)
print(re.findall(r'd+.?d*|d*.?d+', "1-2*(60+(-40.35/5)-(-4*3))"))  # ['1', '2', '60', '40.35', '5', '4', '3']

# 1.3 匹配所有的数字(包含小数包含负号)
print(re.findall(r'-?d+.?d*|d*.?d+', "1-2*(60+(-40.35/5)-(-4*3))"))  # ['1', '-2', '60', '-40.35', '5', '-4', '3']

# 2,匹配一段你文本中的每行的邮箱
# http://blog.csdn.net/make164492212/article/details/51656638 匹配所有邮箱

# 3,匹配一段你文本中的每行的时间字符串 这样的形式:'1995-04-27'

s1 = '''
时间就是1995-04-27,2005-04-27
1999-04-27 创始人
老男孩老师 alex 1980-04-27:1980-04-27
2018-12-08
'''
print(re.findall('d{4}-d{2}-d{2}', s1))  # ['1995-04-27', '2005-04-27', '1999-04-27', '1980-04-27', '1980-04-27', '2018-12-08']

# 4 匹配 一个浮点数
print(re.findall('d+.d*','1.17'))    # ['1.17']

# 5 匹配qq号:腾讯从10000开始:
# print(re.findall('[1-9][0-9]{4,}', '2413545136'))

s1 = '''
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7459977.html" target="_blank">python基础一</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7562422.html" target="_blank">python基础二</a></p>
<p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/9439483.html" target="_blank">Python最详细,最深入的代码块小数据池剖析</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7738630.html" target="_blank">python集合,深浅copy</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8183203.html" target="_blank">python文件操作</a></p>
<h4 style="background-color: #f08080;">python函数部分</h4>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8241942.html" target="_blank">python函数初识</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8259929.html" target="_blank">python函数进阶</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8305011.html" target="_blank">python装饰器</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423526.html" target="_blank">python迭代器,生成器</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423937.html" target="_blank">python内置函数,匿名函数</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8743408.html" target="_blank">python递归函数</a></p>
<p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/8743595.html" target="_blank">python二分查找算法</a></p>

'''
# 1,找到所有的p标签
ret = re.findall('<p>.*?</p>', s1)
print(ret)


# 2,找到所有a标签对应的url
print(re.findall('<a.*?href="(.*?)".*?</a>',s1))

四、第三方模块

五、模块其他说明

5.1、py文件的功能

一个python文件可以有两种用途:

  • 脚本:一个文件就是整个程序,用来被执行
  • 模块:文件中存放着一堆功能,用来被导入使用

python为我们内置了全局变量__name__

  • 当文件被当做脚本执行时:__name__ 等于__main__
  • 当文件被当做模块导入时:__name__等于模块名

作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)

print('from the abcd.py')

__all__ = ['name', 'read1', 'read2']
name = '运维人在路上'


def read1():
    print('abcd模块:', name)


def read2():
    print('abcd模块')
    read1()


def change():
    global name
    name = 'barry'


def func():
    print('正在调试')


if __name__ == '__main__':
    # 在模块文件中测试read1()函数
    # 此模块被导入时 __name__ == abcd 所以不执行
    read1()

5.2、模块的搜索路径

顺序:内存 ----> 内置模块 ---> sys.path

模块的查找顺序

  1. 在第一次导入某个模块时,会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用(ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看)
  2. 如果没有,解释器则会查找同名的内置模块
  3. 如果还没有找到就从sys.path给出的目录列表中依次寻找文件。
#在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。
>>>import sys
>>>sys.path.append('/a/b/c/d')
>>>sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索

#windows下的路径不加r开头,会语法错误
sys.path.insert(0,r'C:UsersAdministratorPycharmProjectsa')

六、包

包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

需要强调的是:

  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

  2. 创建包的目的不是为了运行,而是被导入使用,包只是模块的一种形式而已,包的本质就是一种模块

创建一个包,也会发生三件事:

import aaa
1. 将该aaa包内 __init__.py文件加载到内存.
2. 创建一个以aaa命名的名称空间.
3. 通过aaa. 的方式引用__init__的所有的名字.

6.1、执行文件直接import

image-20210308141731184

1)执行文件如何引用aaa包中__init__.py中的变量和方法

因为执行文件和aaa包在同一级下,直接import aaa就可以将aaa包__init__.py中的变量和方法引入

import aaa
print(aaa.x)
aaa.f1()

2)执行文件如何引入aaa包中m1文件中的变量和方法

# 1. 在执行文件写入 import aaa
# 2. aaa的 __init__ 里面 写 from aaa import m1
# 3. 然后在执行文件  aaa.m1.a
import aaa
print(aaa.m1.a)
aaa.m1.func1()

3)执行文件如何引入aaa包中bbb包中__init__.py文件中的变量和方法

# 1. 在执行文件写入 import aaa
# 2. aaa的 __init__ 里面 写 from aaa import bbb
# 3. 然后在执行文件  aaa.bbb
import aaa
print(aaa.bbb)
print(aaa.bbb.name)
aaa.bbb.func3()

4)执行文件如何引入aaa包中bbb包中mb.py文件中的变量和方法

# 1. 在执行文件写入 import aaa
# 2. 在aaa包的__Init__ 写上 from aaa import bbb  (这样写 bbb包的__init__里面所有的名字都能引用)
# 3. 在bbb包的__Init__ 写上 from aaa.bbb import mb
import aaa
print(aaa.bbb.name)
aaa.bbb.mb.func3()

6.2、执行文件from ... import...

通过这种方式不用设置__init__.py文件

image-20210308144551062

1)执行文件如何引入aaa包中m1中的变量和方法

# 直接from aaa import m1即可
from aaa import m1
m1.func()
print(m1.age)

2)执行文件如何引入aaa包中bbb包中m2.py中的变量和方法

from aaa.bbb.m2 import func1
func1()
# 或者
from aaa.bbb import m2
m2.func1()

6.3、绝对导入和相对导入

绝对导入:以顶级包作为起始

相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

image-20210308150820070

1)执行文件中使用相对引入导入nb包m1,m2,m3中的函数

# 执行文件内容
import nb
nb.f1()
nb.f2()

#nb包中的__init__.py文件
from nb.m1 import f1, f2
from nb.m2 import f3, f4
from nb.m3 import f5, f6
作者:Lawrence

-------------------------------------------

个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!

扫描上面二维码关注我
如果你真心觉得文章写得不错,而且对你有所帮助,那就不妨帮忙“推荐"一下,您的“推荐”和”打赏“将是我最大的写作动力!
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.
原文地址:https://www.cnblogs.com/hujinzhong/p/14504092.html