内容大纲
- 自定义模块
- 模块是什么?
- 为什么要有模块?
- 什么是脚本?
- 模块的分类
- import的使用
- 第一次导入模块执行三件事
- 被导入模块有独立的名称空间
- 为模块起别名
- 导入多个模块
- from ... import ...
- from ... import ...的使用
- from ... import ... 与import对比
- 一行导入多个
- from ... import *
- 模块循环导入的问题
- py文件的两种功能
- 模块的搜索路径
- json pickle 模块
- hashlib模块
具体内容
自定义模块:自定义模块名不能与内置模块名重名。
模块是什么?
将20万行代码全部放在一个py文件中?为什么不行?
- 代码太多,读取代码耗时太长.
2. 代码不容易维护.
将相同的功能封装到一个文件中,这个存储着很多常用的功能的py文件,就是模块。 模块就是文件,存放一堆常用的函数,谁用谁拿。一个函数就是一个功能,把一些常用的函数放在一个py文件中,这个文件就称之为模块。模块就是一些常用功能的集合体。
模块就是一个py文件,是常用的相似功能的集合.
- 代码太多,读取代码耗时太长.
为什么要使用模块?
- 拿来主义,提高开发效率.
便于管理维护.
- 什么是脚本?
- 脚本就是py文件,长期保存代码的文件.
模块的分类:
内置模块,也叫做标准库。此类模块就是python解释器给你提供的,比如我们之前见过的time模块,os模块。标准库的模块非常多(200多个,每个模块又有很多功能)
第三方模块,第三方库。一些python大神写的非常好用的模块,必须通过pip install 指令安装的模块,比如BeautfulSoup, Django,等等。大概有6000多个。
自定义模块,我们自己在项目中定义的一些模块。自己写的一个py文件。定义一个模块其实很简单就是写一个文件,里面写一些代码(变量,函数)即可
# xxx.py (自定义模块) #print('from the xxx.py') name = '太白金星' def read1(): print('xxx模块:',name) def read2(): print('xxx模块') read1() def change(): global name name = 'barry' print(name)
import的使用: 注意:引用模块写在最上面,不要写在函数里。
import 模块 先要怎么样?
import xxx 执行一次xxx这个模块里面的所有代码
模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行。
import语句是可以在程序中的任意位置使用的,且针对同一个模块import多次,为了防止你重复导入,python的优化手段是:第一次引用某个模块,会将这个模块里面的所有代码加载到内存,只要你的程序没有结束,接下来你在引用多少次,它会先从内存中寻找有没有此模块,如果已经加载到内存,就不在重复加载。后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句。
第一次导入模块执行三件事
***
在内存中创建一个以被导入模块名命名的名称空间.(一个py文件有一个名称空间)
执行此名称空间所有的可执行代码(将xxx.py文件中所有的变量与值的对应关系加载到这个名称空间).
通过 模块名. 的方式引用模块里面的内容(变量,函数名,类名等)。 ps:重复导入会直接引用内存中已经加载好的结果
import xxx print(xxx.name) xxx.read1()
被导入模块有独立的名称空间
***
被导入模块有独立的名称空间,它只能操作自己空间中的所有内容,不能操作导入该模块的py文件中的内容。每个模块都是一个独立的全局名称空间,用来定义这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量在被导入时,会与使用者的全局变量冲突。 # import xxx name = 'alex' print(name) print(xxx.name) #结果: alex 太白金星 # import xxx def read1(): print(666) xxx.read1() #结果: xxx模块: 太白金星 # import xxx name = '日天' xxx.change() #barry print(name) # 日天 print(xxx.name) # barry
为模块起别名
**
1 简单,便捷,将长的模块名改为短的模块名
import xxx as m print(m.name) #结果: 太白金星
2,有利于代码的简化.
# 原始写法: result = input('请输入') if result == 'mysql': import mysql1 mysql1.mysql() elif result == 'oracle': import oracle1 oracle1.oracle() # 起别名: result = input('请输入') if result == 'mysql': import mysql1 as m elif result == 'oracle': import oracle1 as m ''' 后面还有很多''' m.db() # 统一接口,归一化思想
导入多个模块:多行导入,易于阅读 ,易于编辑 ,易于搜索 ,易于维护。
import time, os, sys # 这样写不好 # 应该向以下这种写法: import time import os import sys
from ... import ...
from ... import ...的使用
from xxx import name from xxx import read1 from xxx import read2 print(name)#太白金星 print(globals()) read1()#xxx模块: 太白金星
from ... import ... 与import对比
***
from.. import 用起来更方便,在当前名称空间中,直接使用名字就可以了,无需加前缀:xxx.
from xxx import name print(name)
from...import 容易与当前执行文件中的名字产生冲突.
# 容易产生冲突,后者将前者覆盖。执行文件有与模块同名的变量或者函数名,会有覆盖效果 name = 'alex' from xxx import name print(name)#太白金星
当前位置直接使用read1和read2,执行时,仍然以xxx.py文件全局名称空间
***
# from xxx import read1 def read1(): print(666) read1()#666 # from xxx import read1 name = '大壮' read1()#xxx模块: 太白金星 print(globals()) # from xxx import change name = 'Alex' print(name) # 'Alex' change() # 'barry' from xxx import name print(name)#barry
一行导入多个
from tbjx import name,read1,read2 # 这样不好 from tbjx import name from tbjx import read1
from ... import *
把xxx.py中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差。一般千万别么这写,必须要将这个模块中的所有名字全部记住.但是可以配合一个变量使用
# xxx.py __all__ = ['name', 'read1'] # 配合*使用 注:没写到__all__中的变量,可以单独引用 name = '太白金星' def read1(): print('tbjx模块:', name) def read2(): print('tbjx模块') read1() def change(): global name name = 'barry' print(name)
模块循环导入的问题:模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码。在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方。当程序出现了循环/嵌套导入后的异常分析、解决方法如下(了解,以后尽量避免)
#创建一个m1.py print('正在导入m1') from m2 import y x='m1' #创建一个m2.py print('正在导入m2') from m1 import x y='m2' #创建一个run.py import m1 #测试一 执行run.py会抛出异常 正在导入m1 正在导入m2 Traceback (most recent call last): File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module> import m1 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> from m1 import x ImportError: cannot import name 'x' #测试一结果分析 先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错 #测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1 直接执行m1.py抛出异常 正在导入m1 正在导入m2 正在导入m1 Traceback (most recent call last): File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> from m1 import x File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y ImportError: cannot import name 'y' #测试二分析 执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报 # 解决方法: 方法一:导入语句放到最后 #m1.py print('正在导入m1') x='m1' from m2 import y #m2.py print('正在导入m2') y='m2' from m1 import x 方法二:导入语句放到函数中 #m1.py print('正在导入m1') def f1(): from m2 import y print(x,y) x = 'm1' # f1() #m2.py print('正在导入m2') def f2(): from m1 import x print(x,y y = 'm2' #run.py import m1 m1.f1()
py文件的两种功能:
自己使用, 当做脚本,一个文件就是整个程序,用来被执行
被别人引用 ,当做模块使用,文件中存放着一堆功能,用来被导入使用
python为我们内置了全局变量__name__ #用来控制.py文件在不同的应用场景下执行不同的逻辑 print(__name__) 当tbjx.py做脚本: __name__ == __main__ 返回True 当tbjx.py做模块被别人引用时: __name__ == xxx(模块名) # __name__ 根据文件的扮演的角色(脚本,模块)不同而得到不同的结果 1, 模块需要调试时,加上 if __name__ == '__main__': change() # 测试代码 if __name__ == '__main__': change() 2, 作为项目的启动文件需要用.
- 模块的搜索路径
***
- 模块的搜索路径
#import abc abc也是一个内置模块
# python 解释器会自动将一些内置内容(内置函数,内置模块等等)加载到内存中
import sys
print(sys.modules) # 内置内容(内置函数,内置模块等等)
print(sys.path)
#['D:\python_22\day17', 'C:\Python\Python36\python36.zip', 'C:\Python\Python36\DLLs', 'C:\Python\Python36\lib', 'C:\Python\Python36', 'C:\Python\Python36\lib\site-packages']
# 'D:\python_22\day17' 路径是当前执行文件的相对路径
#要想导入dz.py模块: 内存中没有,内置模块也没有,这两个你左右不了,但是sys.path你可以操作.
import sys
sys.path.append(r'D:python_22day16') # sys.path 会自动将你的 当前目录的路径加载到列表中. D:python_22day16 是dz.py的相对路径
import dz
# 如果你想要引用你自定义的模块:
要不你就将这个模块放到当前目录下面,要不你就手动添加到sys.path
模块的查找顺序:
Python中引用模块是按照一定的规则以及顺序去寻找的,这个查询顺序为:先从内存中已经加载的模块进行寻找,找不到再从内置模块中寻找,内置模块如果也没有,最后去sys.path中路径包含的模块中寻找。它只会按照这个顺序从这些指定的地方去寻找,如果最终都没有找到,那么就会报错。
注意的是:我们自定义的模块名不应该与系统内置模块重名
json pickle 模块
序列化模块: 将一种数据结构(list,tuple,dict ....)转化成特殊的序列.
为什么存在序列化?
数据 ----> bytes
只有字符串类型和bytes可以互换.
dict,list..... -------> str <--------> bytes
数据存储在文件中,str(bytes类型)形式存储,比如字典.
数据通过网络传输(bytes类型),str 不能还原回去.
特殊的字符串:序列化.
序列化模块:
json模块 : (重点)
不同语言都遵循的一种数据转化格式,即不同语言都使用的特殊字符串。(比如Python的一个列表[1, 2, 3]利用json转化成特殊的字符串,然后在编码成bytes发送给php的开发者,php的开发者就可以解码成特殊的字符串,然后在反解成原数组(列表): [1, 2, 3])
json序列化只支持部分Python数据结构:dict,list, tuple,str,int, float,True,False,None
dumps loads 主要用于网络传输,但是也可以读写文件,但一般用dump,load读写文件
import json
dic = {'username': '太白', 'password': 123,'status': True}
# 转化成特殊的字符串
st = json.dumps(dic)
print(st,type(st))#{"username": "u592au767d", "password": 123, "status": true} <class 'str'>
st = json.dumps(dic,ensure_ascii=False) #不使用ASCII,使用Unicode,可以显示中文
print(st,type(st))#{"username": "太白", "password": 123, "status": true} <class 'str'>
#反转回去
dic1 = json.loads(st)
print(dic1,type(dic1))#{'username': '太白', 'password': 123, 'status': True} <class 'dict'>
# 写入文件
l1 = [1, 2, 3, {'name': 'alex'}]
# 转化成特殊的字符串写入文件
with open('json文件',encoding='utf-8',mode='w') as f1:
st = json.dumps(l1) #先转化成特殊的字符串
f1.write(st)
# 读取出来还原回去
with open('json文件',encoding='utf-8') as f2:
st = f2.read()
l1 = json.loads(st)
print(l1,type(l1))#[1, 2, 3, {'name': 'alex'}] <class 'list'>
# 特殊的参数??????
博客
dump load 只能用于写入文件,只能写入一个数据结构
import json
l1 = [1, 2, 3, {'name': 'alex'}]
with open('json文件1',encoding='utf-8',mode='w') as f1:
json.dump(l1,f1)
# 读取数据
with open('json文件1',encoding='utf-8') as f2:
l1 = json.load(f2)
print(l1,type(l1))#[1, 2, 3, {'name': 'alex'}] <class 'list'>
# 一次写入文件多个数据怎么做?
# 错误示例:
dic1 = {'username': 'alex'}
dic2 = {'username': '太白'}
dic3 = {'username': '大壮'}
# 写入数据
with open('json文件1',encoding='utf-8',mode='w') as f1:
json.dump(dic1,f1)
json.dump(dic2,f1)
json.dump(dic3,f1)
# 读取数据
with open('json文件1',encoding='utf-8') as f1:
print(json.load(f1))#报错
print(json.load(f1))#报错
print(json.load(f1))#报错
# 正确写法: 用 dumps loads
dic1 = {'username': 'alex'}
dic2 = {'username': '太白'}
dic3 = {'username': '大壮'}
with open('json文件1',encoding='utf-8',mode='w') as f1:
f1.write(json.dumps(dic1) + 'n')
f1.write(json.dumps(dic2) + 'n')
f1.write(json.dumps(dic3) + 'n')
with open('json文件1',encoding='utf-8') as f1:
for i in f1:
print(json.loads(i))
#结果:
{'username': 'alex'}
{'username': '太白'}
{'username': '大壮'}
pickle模块:
只能是Python语言遵循的一种数据转化格式,只能在python语言中使用。
支持Python所有的数据类型,包括实例化对象。
l1 = [1, 2, 3, {'name': 'alex'}]
dumps loads 只能用于网络传输,不能写入文件
import pickle
st = pickle.dumps(l1)
print(st) # bytes类型 b'x80x03]qx00(Kx01Kx02Kx03}qx01Xx04x00x00x00nameqx02Xx04x00x00x00alexqx03se.'
l2 = pickle.loads(st)
print(l2,type(l2))#[1, 2, 3, {'name': 'alex'}] <class 'list'>
dump load 直接写入文件,可以一次写入多个数据
import pickle
dic1 = {'name':'oldboy1'}
dic2 = {'name':'oldboy2'}
dic3 = {'name':'oldboy3'}
f = open('pick多数据',mode='wb')
pickle.dump(dic1,f)
pickle.dump(dic2,f)
pickle.dump(dic3,f)
f.close()
import pickle
f = open('pick多数据',mode='rb')
print(pickle.load(f))
print(pickle.load(f))
print(pickle.load(f))
f.close()
#结果:
{'name': 'oldboy1'}
{'name': 'oldboy2'}
{'name': 'oldboy3'}
#或者:
f = open('pick多数据',mode='rb')
while True:
try:
print(pickle.load(f))
except EOFError:
break
f.close()
# {'name': 'oldboy1'}
# {'name': 'oldboy2'}
# {'name': 'oldboy3'}
#写入函数名
import pickle
def func():
print('in func')
f = open('pick对象',mode='wb')
pickle.dump(func,f) #注意参数顺序 函数名func,文件句柄
f.close()
f = open('pick对象', mode='rb')
ret = pickle.load(f)
print(ret)#<function func at 0x0000020F2F9C1E18> 内存地址
ret()#in func
hashlib模块
包含很多的加密算法. MD5, sha1 sha256 sha512......
用途:
- 密码加密.不能以明文的形式存储密码.密文的形式.
- 文件的校验.
用法:
- 将bytes类型字节 转化成 固定长度的16进制数字组成的字符串.
- 不同的bytes利用相同的算法(MD5)转化成的结果一定不同.
- 相同的bytes利用相同的算法(MD5)转化成的结果一定相同.
- hashlib算法不可逆(MD5中国王晓云破解了).
1.密码加密:
# md5
s1 = 'kfdslfjasdlfgjsdlgkhsdafkshdafjksdfsdkfhjsdafj老fhdskafhsdkjfdsa男孩教育'
import hashlib
ret = hashlib.md5()
ret.update(s1.encode('utf-8'))
print(ret.hexdigest(),type(ret.hexdigest())) # 18f127c24462dd59287798ea5c0c0c2f <class 'str'>
# 相关练习
import hashlib
def MD5(pwd):
ret = hashlib.md5()
ret.update(pwd.encode('utf-8'))
return ret.hexdigest()
def register():
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
password_md5 = MD5(password)
with open('register',encoding='utf-8',mode='a') as f1:
f1.write(f'n{username}|{password_md5}')
register()
def login():
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
password_md5 = MD5(password)
??????
# 普通加密
# 123456: 18f127c24462dd59258898ea5c0c0c2f
# 000000: 18f127c24462dd59258898we5c0c0c2f
s2 = '19890425'
ret = hashlib.md5()
ret.update(s2.encode('utf-8'))
print(ret.hexdigest()) # 6e942d04cf7ceeeba09e3f2c7c03dc44
普通的md5加密,非常简单,几行代码就可以了,但是这种加密级别是最低的,相对来说不很安全。虽然说hashlib加密是不可逆的加密方式,但也是可以破解的,那么他是如何做的呢?你看网上好多MD5解密软件,他们就是用最low的方式,空间换时间。他们会把常用的一些密码比如:123456,111111,以及他们的md5的值做成对应关系,类似于字典,dic = {'e10adc3949ba59abbe56e057f20f883e': 123456},然后通过你的密文获取对应的密码。只要空间足够大,那么里面容纳的密码会非常多,利用空间换取破解时间。 所以针对刚才说的情况,我们有更安全的加密方式:加盐。
# 加盐
s2 = '19890425'
ret = hashlib.md5('太白金星'.encode('utf-8'))
ret.update(s2.encode('utf-8'))
print(ret.hexdigest()) # 84c31bbb6f6f494fb12beeb7de4777e1
# 动态的盐
s2 = '19890425'
ret = hashlib.md5('太白金星'[::2].encode('utf-8')) #比如用户名切片
ret.update(s2.encode('utf-8'))
print(ret.hexdigest()) # 84c31bbb6f6f494fb12beeb7de4777e1
# sha系列 金融类,安全类.用这个级别.
随着sha系列数字越高,加密越复杂,越不易破解,但是耗时越长.
s2 = '198fdsl;fdsklgfjsdlgdsjlfkjsdalfksjdal90425'
ret = hashlib.sha3_512()
ret.update(s2.encode('utf-8'))
print(ret.hexdigest()) # 4d623c6701995c989f400f7e7eef0c4fd4ff15194751f5cb7fb812c7d42a7406ca0349ea3447d245ca29b48a941e2f2f66579fb090babb73eb2b446391a8e102
2.文件的校验
linux中一切皆文件: 文本文件,非文本文件,音频,视频,图片...
无论你下载的视频,还是软件(国外的软件),往往都会有一个md5值,下载完后先测试md5值,然后其与提供的md5值对比,完全一致,则证明没问题
#官网下载pycharm
sha256 : 6217ce726fc8ccd48ec76e9f92d15feecd20422c30367c6dc8c222ab352a3ec6 *pycharm-professional-2019.1.2.exe
#一次性全部update(类比文件)
s1 = '我叫太白金星 今年18岁'
ret = hashlib.sha256()
ret.update(s1.encode('utf-8'))
print(ret.hexdigest()) # 54fab159ad8f0bfc5df726a70332f111c2c54d31849fb1e4dc1fcc176e9e4cdc
#分着update(类比文件)
ret = hashlib.sha256()
ret.update('我叫'.encode('utf-8'))
ret.update('太白金星'.encode('utf-8'))
ret.update(' 今年'.encode('utf-8'))
ret.update('18岁'.encode('utf-8'))
print(ret.hexdigest()) # 54fab159ad8f0bfc5df726a70332f111c2c54d31849fb1e4dc1fcc176e9e4cdc
# low版校验:
def file_md5(path):
ret = hashlib.sha256()
with open(path,mode='rb') as f1: #以rb方式读取
b1 = f1.read() #文件过大会撑爆内存
ret.update(b1)
return ret.hexdigest()
result = file_md5('pycharm-professional-2019.1.2.exe')
print(result) # 6217ce726fc8ccd48ec76e9f92d15feecd20422c30367c6dc8c222ab352a3ec6
# 高大上版:
def file_check(file_path):
with open(file_path,mode='rb') as f1:
sha256 = hashlib.sha256()
while 1:
content = f1.read(1024)
if content:
sha256.update(content)
else:
return sha256.hexdigest()
print(file_check('pycharm-professional-2019.1.1.exe'))
总结
import 三件事, import的名字如果调用? 模块名.的方式调用.
from ... import ... 容易产生冲突,独立的空间.
__name__
问题模块的搜索路径
内存 内置 sys.path
序列化模块:
- json最最常用(两对四个方法就行) 一定要掌握
- pickle(两对四个方法就行) 尽量掌握
hashlib
- 密码的加密 ,文件的校验
- 还没有人评论,欢迎说说您的想法!