模块
什么是模块?
模块是一系列功能的集合体,而函数是某一个功能的集合体,因此模块可以看成是一堆函数的集合体。
模块的四种 形式
在Python中,总共有以下四种形式的模块:
- 自定义模块:如果你自己写一个py文件,在文件内写入一堆函数,则它被称为自定义模块,即使用python编写的.py文件
- 第三方模块:已被编译为共享库或DLL的C或C++扩展
- 内置模块:使用C编写并链接到python解释器的内置模块
- 包:把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
为什么用模块?
- 用第三方或者内置的模块是一种拿来主义,可以极大地提升开发效率。
- 自定义模块,将我们自己程序中用到的公共功能,写入一个python文件,然后程序的各部分组件可以通过导入的方式来引用自定义模块的功能。
如何用模块?
一般我们使用import和from...import...导入模块。
import和from...import...
import 模块名
以time模块为例子:
import time
time.time
# import 发生的3件事情
# 1. 在内存中生成一个叫做time的名称空间
# 2. 运行time.py文件,然后把time.py文件内的名称空间放入time的名称空间内
# 3. 把time的名称空间指向 import和from...impot.py(当前导入time模块的文件) 的名称空间中
# 使用import time导入的时候,使用方法只能 time.方法名() ,不能直接方法名
from...import...
以gmtime方法为例:
from time import gmtime
# from time import * # 所有(等同于import time),不推荐使用,因为很容易造成变量名冲突
print(gmtime())
# from...import... 发生的3件事情
# 1. 在内存中生成一个叫做time的名称空间
# 2. 运行time.py文件,让后把time.py文件内的名称空间放入time的名称空间内
# 3. 把gtime方法指向 import和from...import.py(当前导入time模块的文件) 的名称空间内
# 使用from...import...导入的时候,直接使用方法名就可以
- 优点:不用加前缀,代码更加精简
- 缺点:容易与当前执行文件中名称空间中的名字冲突
# spam.py 模块文件
__all__ = ['money', 'read1'] # 只允许导入'money'和'read1'
# run.py 执行文件
from spam import * # 导入spam.py内的所有功能,但会受限制于__all__
import和from...import...的异同
相同点:
- 两者都会执行模块对应的文件,两者都会产生模块的名称空间
- 两者调用功能时,需要跑到定义时寻找作用域关系,与调用位置无关
不同点
- import需要加前缀;from...import...不需要加前缀
循环导入问题
什么是循环导入
有两个文件m1.py和m2.py
# m1.py
from m2 import y
x = 'm2'
# m2.py
from m1 import x
y = 'm1'
- 当我们执行m1.py
- m1.py运行到
from m2 import y
,要运行m2.py - m2.py运行到
from m1 import x
,要运行m1.py - 进入死循环
解决方案
我们可以使用函数定义阶段只识别语法的特性解决循环导入的问题,我们也可以从本质上解决循环导入的问题,但是最好的解决方法是不要出现循环导入。
1 方案一
# m1.py
def func1():
from m2 import x
print(x)
y = 'm1'
# m2.py
def func1():
from m1 import y
print(y)
x = 'm2'
2 方案二
# m1.py
y = 'm1'
from m2 import x
# m2.py
x = 'm2'
from m1 import y
模块搜索路径的顺序
内存 --> 内置 --> 自定制 --> 环境变量(sys.path)
Pthon文件的两种用途
- 执行文件: 当前运行的文件
- 模块文件: 被当做模块导入的文件
if __name__ == '__main__'
在执行文件中运行__name__ == '__main__'
, 在被当做模块导入的时候__name__ == 模块文件名
包
什么是包
包是模块的一种形式,包的本质就是一个含有__init__.py
的文件的文件夹。(init让普通的文件夹成为一个包, 导包就是导init)
为什么要有包
模块的第一个版本只有10个功能,但是未来在扩展版本的时候,模块名和用法应该最好不要去修改,但是这只是对使用者友好,而由于版本扩展,文件越来越大,模块设计者对模块的管理、维护会越来越复杂,因此我们可以使用包来扩展模块的功能。
如何用包
1 模块和包
导入模块发生的三件事:
- 创建一个包的名称空间
- 执行py文件,将执行过程中产生的名字存放于名称空间中。
- 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的
导入包发生的三件事:
- 创建一个包的名称空间
- 由于包是一个文件夹,无法执行包,因此执行包下的.py文件,将执行过程中产生的名字存放于包名称空间中(即包名称空间中存放的名字都是来自于.py)
- 在当前执行文件中拿到一个名字aaa,aaa是指向包的名称空间的
导入包就是在导入包下的.py,并且可以使用以下两种方式导入:
- import ...
- from ... import...
扩展模块功能
我们如果需要扩展aaa.py模块,需要建立一个aaa的目录文件,并且删除aaa.py文件,将aaa.py修改成m1.py和m2.py两个文件,让模块的功能使用方法不改变。
修改__init__.py文件
导入包内包
导入包内包的模块
绝对导入和相对导入
绝对导入:
直接填入路径
相对导入:
- .代表当前被导入文件所在的文件夹
- ..代表当前被导入文件所在的文件夹的上一级
- ...代表当前被导入文件所在的文件夹的上一级的上一级
注意事项
- 包内所有的文件都是被导入使用的,而不是被直接运行的
- 包内部模块之间的导入可以使用绝对导入(以包的根目录为基准)与相对导入(以当前被导入的模块所在的目录为基准),推荐使用相对导入
- 当文件是执行文件时,无法在该文件内用相对导入的语法,只有在文件时被当作模块导入时,该文件内才能使用相对导入的语法
- 凡是在导入时带点的,点的左边都必须是一个包,
import aaa.bbb.m3.f3
错误
random模块
取随机数
**牢记**
random.random() # (0-1)的随机数
random.randint(0,100) # [0-100]的整数
random.choice() # 从容器中随机选择一个
random.sample() # 从容器中随机选择多个
random.shuffle() # 打乱容器
time模块
时间
# 时间戳
print(time.time()) # 1565922341.6068554
# 格式化时间
# year month day X
print(time.strftime('%Y_%m_%d %X')) # 2019_08_16 10:26:36
# 结构化时间
print(time.localtime()) # 当地时间(中国)
print(time.gmtime()) # 标准时间
# 三种格式时间的转换(没必要记)
# 结构化时间转格式化时间
print(time.strftime('%Y-%m-%d %X', time.localtime()))
# 格式化时间转结构化时间
print(time.strptime('2019-08-16 10:30:44', '%Y-%m-%d %X'))
# 结构化时间转时间戳
print(time.mktime(time.localtime()))
# 时间戳转结构化时间
print(time.localtime(time.time()))
# 重点
time.time()
time.sleep(2) #等待(时间)
datetime模块
时间的修改
import datetime
print(datetime.datetime.now()) # 打印时间
now = datetime.datetime.now()
print(now+datetime.timedelta(3)) # 默认+3天
print(now+datetime.timedelta(hours=3)) # 3小时
print(now+datetime.timedelta(minutes=3)) # 3分钟
os 模块
与操作系统交互,可以操控文件
import os
res = os.listdir(r'D:\上海Python11期视频\python11期视频\day 17') # 列出所有文件
print(os.getcwd()) # 获取当前文件目录
# 支持不同的平台(windows,ios,andirod,linux,unix)
res = os.path.join(r'D:\上海Python11期视频\python11期视频\day 17\m1\bbb','m5.py') # 拼接文件路径
sys 模块
与Python解释器交互
sys.modules.keys() # 返回所有已经导入的模块列表
sys.version # 获取Python解释程序的版本信息
json 模块
假如我们用python写了一个程序,用java写了一门程序,这两个程序需要数据之间交流,就需要规定一种多种语言通用的数据类型,json数据类型(又称json串)
序列化
把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening。
简单的来说就是从python的字典(最常用)变成json串 (dump方法)
序列化的优点:
- 持久保存状态:内存是无法永久保存数据的,当程序运行了一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间的数据(有结构)都被清空了。但是在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。
- 跨平台数据交互:序列化时不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
Json序列化并不是python独有的,json序列化在java等语言中也会涉及到,因此使用json序列化能够达到跨平台传输数据的目的。
json数据类型和python数据类型对应关系表
Json类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
520.13 | int或float |
true/false | True/False |
null | None |
反序列化
从json串变成python的字典(最常用) (load方法)
# 序列化
with open('Json序列化对象.json', 'w') as fw:
json.dump(struct_data, fw)
# 反序列化
with open('Json序列化对象.json') as fr:
data = json.load(fr)
print(data)
注意: json只针对dict/list
pickle 模块
Pickle序列化和所有其他编程语言特有的序列化问题一样,它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,即不能成功地反序列化也没关系。但是pickle的好处是可以存储Python中的所有的数据类型,包括对象,而json不可以。
# 序列化(注意:pickle模块需要使用二进制存储,即'wb'模式存储)
with open('Pickle序列化对象.pkl', 'wb') as fw:
pickle.dump(struct_data, fw)
# 反序列化
with open('Pickle序列化对象.pkl', 'rb') as fr:
pickle = pickle.load(fr)
print(data)
hashlib 模块
密码加密:无论你丢什么字符串,他都会返回一串 固定长度的字符串(俗称md5值)
什么是hash
hash是一种算法(Python3.版本里使用hashlib模块代替了md5模块和sha模块,主要提供 SHA1、SHA224、SHA256、SHA384、SHA512、MD5 算法),该算法接受传入的内容,经过运算得到一串hash值。
hash值的特点:
- 只要传入的内容一样,得到的hash值一样,可用于非明文密码传输时密码校验
- 不能由hash值返解成内容,即可以保证非明文密码的安全性
- 只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的,可以用于对文本的哈希处理
hash加密算法虽然看起来很厉害,但是他是存在一定缺陷的,即可以通过撞库可以反解
hmac 模块
为了防止密码被撞库,我们可以使用python中的另一个hmac 模块,它内部对我们创建key和内容做过某种处理后再加密。
如果要保证hmac模块最终结果一致,必须保证:
- hmac.new括号内指定的初始key一样
- 无论update多少次,校验的内容累加到一起是一样的内容
注意hmac模块只接受二进制数据的加密
logging 模块
日志
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)
可在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为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用户输出的消息
# 1. 生成一个logger对象
logger = logging.getLogger('nick')
# 2. 生成格式
formmater1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',)
formmater2 = logging.Formatter('%(asctime)s : %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',)
formmater3 = logging.Formatter('%(name)s %(message)s',)
# 3. 生成一个文件对象
h1 = logging.FileHandler('h1.log')
h2 = logging.FileHandler('h2.log')
sm = logging.StreamHandler() #打印带屏幕上
# 4. 文件绑定格式
h1.setFormatter(formmater1)
h2.setFormatter(formmater2)
sm.setFormatter(formmater3)
# 5. 绑定文件
logger.addHandler(h1)
logger.addHandler(h2)
logger.addHandler(sm)
# 6. 控制级别
logger.level = 50
# 7. 使用
logger.debug('调试信息') # 10
logger.info('正常信息') # 20
logger.warning('警告信息') # 30
logger.error('报错信息') # 40
logger.critical('严重错误信息') # 50
日志文件配置
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()指定的名字;lineno为调用日志输出函数的语句所在的代码行
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.dirname(os.path.abspath(__file__))) # log文件的目录,需要自定义文件路径 # atm
logfile_dir = os.path.join(logfile_dir, 'log') # C:\Users\oldboy\Desktop\atm\log
logfile_name = 'log.log' # log文件名,需要自定义路径名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir): # C:\Users\oldboy\Desktop\atm\log
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name) # C:\Users\oldboy\Desktop\atm\log\log.log
# 定义日志路径 结束
# log配置字典
LOGGING_DIC = {
'version': 1, # 版本
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format #上面日志输出的格式
},
'simple': {
'format': simple_format #上面日志输出的格式
},
},
'filters': {}, # filter可以不定义
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG', # 日志级别
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'INFO',
'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配置。如果''设置为固定值logger1,则下次导入必须设置成logging.getLogger('logger1')
'': {
# 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': False, # 向上(更高level的logger)传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态
return logger
if __name__ == '__main__':
load_my_logging_cfg()
numpy模块
numpy官方文档:https://docs.scipy.org/doc/numpy/reference/?v=20190307135750
numpy是Python的一种开源的数值计算扩展库。这种库可用来存储和处理大型numpy数组,比Python自身的嵌套列表结构要高效的多(该结构也可以用来表示numpy数组)。
numpy库有两个作用:
- 区别于list列表,提供了数组操作、数组运算、以及统计分布和简单的数学模型
- 计算速度快,甚至要由于python内置的简单运算,使得其成为pandas、sklearn等模块的依赖包。高级的框架如TensorFlow、PyTorch等,其数组操作也和numpy非常相似。
pandas模块
pandas官方文档:https://pandas.pydata.org/pandas-docs/stable/?v=20190307135750
pandas基于Numpy,可以看成是处理文本或者表格数据。pandas中有两个主要的数据结构,其中Series数据结构类似于Numpy中的一维数组,DataFrame类似于多维表格数据结构。
pandas是python数据分析的核心模块。它主要提供了五大功能:
- 支持文件存取操作,支持数据库(sql)、html、json、pickle、csv(txt、excel)、sas、stata、hdf等。
- 支持增删改查、切片、高阶函数、分组聚合等单表操作,以及和dict、list的互相转换。
- 支持多表拼接合并操作。
- 支持简单的绘图操作。
- 支持简单的统计分析操作。
matplotlib模块
matplotlib官方文档:https://matplotlib.org/contents.html?v=20190307135750
matplotlib是一个绘图库,它可以创建常用的统计图,包括条形图、箱型图、折线图、散点图、饼图和直方图。
typing模块
typing模块的作用
- 类型检查,防止运行时出现参数和返回值类型不符合。
- 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
- 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。
- 注意:typing模块只有在python3.5以上的版本中才可以使用,pycharm目前支持typing检查
typing常用类型
- int、long、float: 整型、长整形、浮点型
- bool、str: 布尔型、字符串类型
- List、 Tuple、 Dict、 Set:列表、元组、字典、集合
- Iterable、Iterator:可迭代类型、迭代器类型
- Generator:生成器类型
collections模块
collections是Python内建的一个集合模块,提供了许多有用的集合类。
namedtuple
我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成
deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。
defaultdict
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。
除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。
OrderedDict
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict
注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序
OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key
Counter
Counter是一个简单的计数器,例如,统计字符出现的个数
Counter实际上也是dict的一个子类,上面的结果可以看出,字符'g'、'm'、'r'各出现了两次,其他字符各出现了一次。