首页 > temp > python入门教程 >
-
python--高级语法 10
day:15
1、自定义模块:
什么是模块:本质就是.py文件,封装语句的最小单位。
- 多个模块对应包,在代码中包就是一个文件夹
自定义模块:实际上就是定义.py,其中可以包含:变量定义,可执行语句,for循环,函数定义等等,他们统称模块的成员。
2、模块的运行方式:
-
脚本方式:直接用解释器执行。或者PyCharm中右键运行。
-
模块方式:被其他的模块导入。为导入它的模块提供资源(变量,函数定义,类定义等)。
''' 测试自定义模块的导入 ''' #自定义模块被其他模块导入时,其中的可执行语句会路基执行 import a [2, 11, 15, 7] Process finished with exit code 0 #******************************************************* #a.py nums=[2,11,15,7] def fun(nums): print(nums) fun(nums)
思考:自定义模块被其他模块导入时,其中的可执行语句会路基执行,如何控制可执行语句不执行?
-----python中提供一种可以判断自定义模块是属于开发阶段还是使用阶段:
__name__
就是通过 if __name__ == '__main__': 来实现
把可执行语句放在主函数main中执行,
自定义模块在右键运行时,print(__name__) 的属性值 __main__,但是在以模块方式被导入时,在其他脚本中右键运行,print(__name__) 的属性值就是模块名了 如a ,
所以通过if __name__ == '__main__': 可以控制被调用的模块中的可执行语句不被执行。
3、__name__
属性的使用:
-
在脚本方式运行时(右键执行),
__name__
是固定的字符串:__main__
nums=[2,11,15,7]
def fun(nums):
print(nums)
fun(nums)
print(__name__)
[2, 11, 15, 7]
__main__
Process finished with exit code 0
-
在以模块方式被导入时,
__name__
就是本模块的名字。 -
在自定义模块中对
__name__
进行判断,决定是否执行可执行语句:开发阶段,就执行,使用阶段就不执行。
#开发阶段--会执行
nums=[2,11,15,7]
def fun(nums):
print(nums)
#__name__
if __name__ == '__main__':
fun(nums)
[2, 11, 15, 7]
Process finished with exit code 0
#**********************************************************
#使用阶段(调用阶段)---不执行
import a
#右键执行
Process finished with exit code 0
4、系统导入模块的路径
- 内存中:如果之前成功导入过某个模块,直接使用已经存在的模块
- 内置路径中:安装路径下:Lib
- PYTHONPATH:import时寻找模块的路径。
- sys.path:是一个路径的列表。
如果上面都找不到,就报错。
通过动态修改sys.path的方式将自定义模块添加到sys.path中。
os.path.dirname():获取某个路径的父路径。通常用于获取当前模块的相对路径
#使用相对路径找到aa文件夹
#print(__file__) #当前文件的绝对路径
#使用os模块获取一个路径的父路径
#os.path.dirname()
#添加 a.py所在的路径到 sys.path 中
#sys.path.append()
import sys
import os
sys.path.append(os.path.dirname(__file__) + '/aa')
5、导入模块的多种方式:
- import xxx:导入一个模块的所有成员
- import aaa,bbb:一次性导入多个模块的成员。不推荐这种写法,分开写。
- from xxx import a:从某个模块中导入指定的成员。
- from xxx import a,b,c:从某个模块中导入多个成员。
- from xxx import *:从模块中导入所有成员。
5.1、import xxx 和 from xxx import * 的区别:
第一种方式在使用其中成员时,必须使用模块名作为前缀。不容易产生命名冲突。
第二种方式在使用其中成员时,不用使用模块名作为前缀,直接使用成员名即可。但是容易产生命名冲突。在后定义的成员生效(把前面的覆盖了。)
5.2、怎么解决名称冲突的问题?
- 改用import xxx这种方式导入。
- 自己避免使用同名
- 使用别名解决冲突
5.3、使用别名:alias
给成员起别名,避免名称冲突。
from my_module import age as a
给模块起别名,目的简化书写。
import my_module as m
5.4、from xxx import * 控制成员被导入
默认情况下,所有的成员都会被导入。
__all__
是一个列表,用于表示本模块可以被外界使用的成员。元素是成员名的字符串。
注意:
__all__
只是对from xxx import *这种导入方式生效。其余的方式都不生效。
5.5、相对导入
针对某个项目中的不同模块之间进行导入,称为相对导入。
只有一种格式:
from 相对路径 import xxx
相对路径:包含了点号的一个相对路径。
. 表示的是当前的路径。
..表示的是父路径。
...表示的是父路径的父路径。
# 相对导入同项目下的模块
# from ..z import zz # 容易向外界暴露zz模块
from ..z.zz import *
# 不使用相对导入的方式,导入本项目中的模块
# 通过当前文件的路径找到z的路径
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(__file__)) + '/z')
from zz import *
6、内置模块
random
此模块提供了和随机数获取相关的方法:
- random.random():获取 [0.0,1.0) 范围内的浮点数
- random.randint(a,b):获取 [a,b] 范围内的一个整数
- random.uniform(a,b):获取 [a,b) 范围内的浮点数
- random.shuffle(x):把参数指定的数据中的元素打乱。参数必须是一个可变的数据类型。
- random.sample(x,k):从x中随机抽取k个数据,组成一个列表返回。
import random
#random.random():获取 **[0.0,1.0)** 范围内的浮点数
l1=random.random()
print(l1)
#random.uniform(a,b):获取 **[a,b)** 范围内的浮点数
l2=random.uniform(3,5)
print(l2)
#random.randint(a,b):获取 **[a,b]** 范围内的一个整数
l3=random.randint(1,3)
print(l3)
#random.shuffle(x):把参数指定的数据中的元素打乱。参数必须是一个可变的数据类型。
l4=list(range(10))
print(l4)
random.shuffle(l4)
print(l4)
#random.sample(x,k):从x中随机抽取k个数据,组成一个列表返回
l5=(1,2,3)
l6=random.sample(l5,2)
print(l6)
0.41410924095974666
4.725643623367955
2
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 4, 2, 6, 7, 8, 1, 0, 3, 9]
[1, 3]
Process finished with exit code 0
time:和时间相关
封装了获取时间戳和字符串形式的时间的一些方法。
- time.time():获取时间戳
- time.gmtime([seconds]):获取格式化时间对象:是九个字段组成的
- time.localtime([seconds]):获取格式化时间对象:是九个字段组成的
- time.mktime(t):时间对象 -> 时间戳
- time.strftime(format[,t]):把时间对象格式化成字符串
- time.strptime(str,format):把时间字符串转换成时间对象
import time
#(1)获取时间戳 #时间戳:从时间元年(1970 1 1 00:00:00)到现在经过的秒数。
l1=time.time()
print(l1)
#(2)获取格式化时间对象:是九个字段组成的。
print(time.gmtime()) #GMT
print(time.localtime())
# 默认参数是当前系统时间的时间戳。
print(time.gmtime(1)) # 时间元年过一秒后,对应的时间对象
#(3)格式化时间对象 转换为 字符串
s=time.strftime("year:%Y %m %d %H:%M:%S")
print(s)
#(4)把时间字符串 转换成 时间对象
time_obj = time.strptime('2010 10 10','%Y %m %d')
print(time_obj)
#(5)时间对象 -> 时间戳
t1 = time.localtime() # 时间对象
t2 = time.mktime(t1) # 获取对应的时间戳
print(t2)
print(time.time())
1663252449.9508088
time.struct_time(tm_year=2022, tm_mon=9, tm_mday=15, tm_hour=14, tm_min=34, tm_sec=9, tm_wday=3, tm_yday=258, tm_isdst=0)
time.struct_time(tm_year=2022, tm_mon=9, tm_mday=15, tm_hour=22, tm_min=34, tm_sec=9, tm_wday=3, tm_yday=258, tm_isdst=0)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=1, tm_wday=3, tm_yday=1, tm_isdst=0)
year:2022 09 15 22:34:09
time.struct_time(tm_year=2010, tm_mon=10, tm_mday=10, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=283, tm_isdst=-1)
1663252859.0
1663252859.9694235
Process finished with exit code 0
time模块三大对象之间的转换关系:
其他方法:
- time.sleep(x):休眠x秒.
datetime:日期时间相关
封装了一些和日期,时间相关的类,主要有:
- date: 需要年,月,日三个参数
- time: 需要时,分,秒三个参数
- datetime: 需要年,月,日,时,分,秒六个参数.
- timedelta: 需要一个时间段.可以是天,秒,微秒
获取以上类型的对象,主要作用是和时间段进行数学运算.
timedelta可以和以下三个类进行数学运算:
- datetime.time,datetime.datetime,datetime.timedelta
import datetime
#(1):date类: 需要年,月,日三个参数
d=datetime.date(2010,10,10) #把年月日三个参数转换成一个对象
print(d)
#获取date对象的各个属性
print(d.year)
print(d.month)
print(d.day)
#(2):time类: 需要时,分,秒三个参数
t=datetime.time(10,58,59)
print(t)
#获取time对象的各个属性
print(t.hour)
print(t.minute)
print(t.second)
#(3):datetime 类: 需要年,月,日,时,分,秒六个参数.
dt=datetime.datetime(2022,9,15,23,58,59)
print(dt)
#获取datetime对象的各个属性
#(4):timedelta类: 理解:时间的变化量 需要一个时间段.可以是天,秒,微秒
td=datetime.timedelta(days=1)
print(td)
#参与数学运算
#创建时间对象:
l1=datetime.date(2010,10,10)
res=l1+td
print(res)
2010-10-10
2010
10
10
10:58:59
10
58
59
2022-09-15 23:58:59
1 day, 0:00:00
2010-10-11
Process finished with exit code 0
练习:
- 显示当前日期前三天是什么时间.
#练习:显示当前日期前三天是什么时间.
year=int(input("输入年份:"))
month=int(input("输入年份:"))
day=int(input("输入年份:"))
#创建指定年月日的date对象
d=datetime.date(year,month,day)
# 创建三天 的时间段
td = datetime.timedelta(days=3)
res = d - td
print(res)
输入年份:2020
输入年份:10
输入年份:4
2020-10-01
Process finished with exit code 0
- 显示任意一年的二月份有多少天.
#练习:计算某一年的二月份有多少天?
# 普通算法:根据年份计算是否是闰年.是:29天,否:28
# 用datetime模块. # 首先创建出指定年份的3月1号.然后让它往前走一天.
year=int(input("输入年份:"))
# 创建指定年份的date对象
d=datetime.date(year,3,1)
# 创建一天 的时间段
td = datetime.timedelta(days=1)
res = d - td
print(res.day)
输入年份:2014
28
Process finished with exit code 0
os:操作系统接口
此模块提供了灵活的和操作系统相关的函数.
1、删除:
- os.remove(file_path) : 删除文件
-
os.rmdir(dir_path) : 删除空文件夹
- 删除非空文件夹使用另一个模块 : shutil
- shutil.rmtree(path)
- os.removedirs(name) : 递归删除空文件夹,先删除最里面的目录 然后继续删除父级
2、重命名
- os.rename(src, dst) : 文件,目录重命名,目标不能事先存在.
#os:操作系统接口
import os
#和文件操作相关(重命名,删除等)
#删除
#os.remove(r'a.txt')
#删除目录,必须时空目录
#os.removedirs('aa') #OSError: [WinError 145] 目录不是空的。: 'aa'
#使用shutil模块可以删除待内容的目录
import shutil
shutil.rmtree('aa')
#重命名
#os.rename('a.txt','b.txt')
3、和路径相关的属性,更多相关的操作被封装在os.path这个模块中.
- os.curdir : 当前路径
- os.sep : 路径分隔符
- os.altsep : 备用的分隔符
- os.extsep : 扩展名分隔符
- os.pathsep : 路径分隔符
- os.linesep : 行分隔符,不要在写文件的时候,使用这个属性
os.path 模块
此模块实现了一些在路径操作上的方法.
-
os.path.dirname(path) : 返回一个路径中的父目录部分
如果只是一个盘符,或者是以路径分隔符结尾的字符串,则整体返回.
否则返回的是路径中的父目录部分.
样例:
import os
print(os.path.dirname('.')) #
print(os.path.dirname('/aa/')) # /aa
print(os.path.dirname('D:/test')) # D:/
print(os.path.dirname('D:/')) # D:/
-
os.path.basename(path) : 返回path指定的路径的最后一个内容.
如果只是一个盘符,或者是以路径分隔符结尾的字符串,则返回空;
否则返回的是路径中的最后一部分内容.
样例:
import os print(os.path.basename('.')) # .
print(os.path.basename('/aa')) # aa
print(os.path.basename('/aa/')) #
print(os.path.basename('D:/test')) # test
-
os.path.split(path) : 把路径中的路径名和文件名切分开 ,结果是元祖
返回一个元组,第二个元素表示的是最后一部分的内容,第一个元素表示的是剩余的内容.
如果只是一个盘符或者是以路径分隔符结尾的字符串,则第二个元素为空.
否则第二个元素就是最后一部分的内容.
如果path中不包含路径分隔符,则第一个元素为空.
样例:
import os
print(os.path.split('D:/')) # ('D:/', '')
print(os.path.split('.')) # ('', '.')
print(os.path.split('/aa')) # ('/', 'aa')
-
os.path.join(path,*paths) : 连接若干个路径为一个路径.
如果路径中有绝对路径,则在这个路径之前的路径都会被丢弃,而从这个路径开始往后拼接.
Windows中盘符一定要带,否则不认为是一个盘符.
样例:
res = os.path.join('aa','bb','cc')
print(res) # aa\bb\cc
res2 = os.path.join('D:/','test')
print(res2) # D:/test
-
os.path.abspath(path) :返回一个路径的绝对路径.
如果参数路径是相对的路径,就把当前路径和参数路径的组合字符串当成结果返回.
如果参数路径已经是绝对路径,就直接把参数返回.
如果参数路径以/开始,则把当前盘符和参数路径连接起来组成字符串返回.
注意:
此方法只是简单的将一个拼接好的字符串返回,并不会去检查这个字符串表示的文件是否存在.
样例:
import os
print(os.path.abspath('aa')) # D:\PycharmProjects\test03\test01\aa
print(os.path.abspath('D:/test/aa')) # D:\test\aa
print(os.path.abspath('/bb')) # D:\bb
判断功能
os.path.exists(path) : 判断路径是否真正存在.
os.path.isabs(path) : 判断是否是绝对路径
os.path.isfile(path) : 判断是否是文件
os.path.isdir(path) : 判断是否是目录
sys:和python解释器相关
提供了解释器使用和维护的变量和函数.
-
sys.argv :当以脚本方式执行程序时,从命令行获取参数.
argv[0]表示的是当前正在执行的脚本名.argv[1]表示第一个参数,以此类推.
样例:
有脚本 test.py 内容如下:
import sys print('脚本名称:',sys.argv[0])
print('第一个参数是:',sys.argv[1])
print('第二个参数是:',sys.argv[2])
使用命令行方式运行该脚本: python test.py hello world
解释器去寻找模块的路径
-
sys.path :系统寻找模块的路径.可以通过PYTHONPATH来进行初始化.
由于是在程序执行的时候进行初始化的,所以,路径的第一项path[0]始终是调用解释器的脚本所在的路径.如果
是动态调用的脚本,或者是从标准输入读取到脚本命令,则path[0]是一个空字符串.程序中可以随时对这个路径
进行修改.以达到动态添加模块路径的目的.
json模块
JSON : JavaScript Object Notation ------Java脚本对象标记语言.
已经成为一种简单的数据交换格式.
序列化和反序列化
序列化 : 将其他数据格式转换成json字符串的过程.
反序列化 : 将json字符串转换其他数据类型的过程.
涉及到的方法:
-
json.dumps(obj) : 将obj转换成json字符串返回到内存中. (dumps是从内存到内存)
-
json.dump(obj,fp) : 将obj转换成json字符串并保存在fp指向的文件中.
-
json.loads(s) : 将内存中的json字符串转换成对应的数据类型对象
-
json.load(f) : 从文件中读取json字符串,并转换回原来的数据类型.
注意:
-
json并不能序列化所有的数据类型:例如:set.
-
元组数据类型经过json序列化后,变成列表数据类型.
-
json文件通常是一次性写入,一次性读取.但是可以利用文件本身的方式实现:一行存储一个序列化json字符串,在反序列化时,按行反序列化即可.
内存中的数据:结构化的数据:
磁盘上的数据:线性数据:
序列化比喻:
以下是Python中的可以被序列化的数据类型:
补充:写入文件必须要字符串
#t---text
#b---binary 字节
#a---append 追加写
#w---write 覆盖写
#r---read
#默认是 rt
#写入文件必须要字符串
with open('a.txt',mode='wt',encoding='utf-8')as f :
f.write(10)
TypeError: write() argument must be str, not int
Process finished with exit code 1
序列化案例:
1、考察 json.dumps
import json
s=json.dumps([1,2,3]) #把指定的对象转换成json格式的字符串,并保存在内存中
print(type(s))
print(s)
s2=json.dumps((1,2,3)) #元祖序列化后变成列表
print(type(s2))
print(s2)
s3=json.dumps(10)
print(type(s3))
print(s3)
s4=json.dumps({'name':'andy','age':10})
print(type(s4))
print(s4)
<class 'str'>
[1, 2, 3]
<class 'str'>
[1, 2, 3]
<class 'str'>
10
<class 'str'>
{"name": "andy", "age": 10}
Process finished with exit code 0
2、考察 json.dump-----将json 结果写入文件中
#将json 结果写入文件中
with open('a.txt',mode='at',encoding='utf-8')as f:
json.dump([1,2,3],f)
反序列化案例:
3、考察json.loads
#反序列化
s1=json.dumps([1,2,3])
s2=json.loads(s1)
print(type(s2))
print(s2)
<class 'list'>
[1, 2, 3]
Process finished with exit code 0
4、考察 json.load
#如何从文件中反序列化
with open('a.txt',encoding='utf-8')as f:
res=json.load(f)
print(type(res))
print(res)
<class 'list'>
[1, 2, 3]
Process finished with exit code 0
pickle 序列化
python专用的序列化模块. (屁寇)
pickle:
将python中所有的数据类型,转化成字节串 序列化过程
将字节串,转化成python中的数据类型 反序列化过程
pickle模块中的方法基本和json模块中的方法一样.只是适用的数据类型范围比json更广泛.
-
将其他数据类型序列化成字节
-
将字节反序列化成其他数据类型
-
将其他数据类型序列化成字节并写入文件
-
从文件中读取字节并反序列化成其他数据类型
区别在于:
json:
1.不是所有的数据类型都可以序列化.结果是字符串.
2.不能多次对同一个文件序列化.
3.json数据可以跨语言
pickle:
1.所有python类型都能序列化,结果是字节串.
2.可以多次对同一个文件序列化
3.不能跨语言.
案例:
1.在内存中序列化和反序列化
import pickle
#序列化列表类型
bys=pickle.dumps([1,2,3])
print(type(bys)) #<class 'bytes'>
print(bys) #b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
#序列化元祖类型
s1=pickle.dumps((1,2,3))
print(s1) #b'\x80\x03K\x01K\x02K\x03\x87q\x00.'
#反序列化后,保存了元祖的数据类型 (json不行,json反序列化元祖后,类型是列表)
res=pickle.loads(s1)
print(type(res)) #<class 'tuple'>
#序列化集合类型
s2=pickle.dumps(set('abc'))
res=pickle.loads(s2)
print(type(res)) #<class 'set'>
2、在文件中序列化和反序列化
#把pickle 序列化内容写入文件中
with open('c.txt',mode='wb')as f:
pickle.dump([1,2,3,4],f)
#从文件中反序列化pickle数据
with open('c.txt',mode='rb')as f:
q=pickle.load(f)
print(type(q))
print(q)
<class 'list'>
[1, 2, 3, 4]
Process finished with exit code 0
**hashlib: **加密模块
封装一些用于加密的类.
md5()等
import hashlib
print(dir(hashlib))
['md5', 'new', 'pbkdf2_hmac', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'sha512', 'shake_128', 'shake_256']
加密的目的:用于判断和验证,而并非解密.
特点:
- 把一个大的数据,切分成不同块,分别对不同的块进行加密,再汇总的结果,和直接对整体数据加密的结果是一致的.
- 单向加密,不可逆.
- 原始数据的一点小的变化,将导致结果的非常大的差异,'雪崩'效应.
使用摘要算法加密一个数据的三大步骤:
-
获取一个加密算法对象.
-
调用加密对象的update方法给指定的数据加密.
-
调用加密对象的digest或者是hexdigest获取加密后的结果.
样例:
import hashlib
#获取一个加密对象
m=hashlib.md5()
#使用加密对象的update,进行加密
m.update('abc中文'.encode('utf-8')) # 对参数进行加密,参数必须是字节类型
#或 m.update(b'abc')
# 通过hexdigest获取加密后的字符串
res=m.hexdigest()
print(res)
# 字节形式的结果
print(m.digest())
1af98e0571f7a24468a85f91b908d335
b'\x1a\xf9\x8e\x05q\xf7\xa2Dh\xa8_\x91\xb9\x08\xd35'
Process finished with exit code 0
简化写法
加密对象的update方法可以调用多次,意味着在在前一次的update结果之上,再次进行加密.如果只是一次更新的话,
还可以直接把数据当成参数传递给构造方法.
例如:
m = md5()
m.update(data)
res = m.hexdigest()
和下面的语句是等价的:
res = md5(data).hexdigest()
这种在创建加密对象的时候,就指定初始化的数据,称为 salt (盐).
目的就是为了让加密的结果更加复杂.
练习:
需求:
把用户名和密码信息加密后,通过序列化的方式存储到本地文件中.
并通过控制台输入信息进行验证.
import hashlib
#注册,登录程序:
#加密方法
def get_md5(username,password):
m=hashlib.md5()
m.update(username.encode('utf-8'))
m.update(password.encode('utf-8'))
return m.hexdigest()
#注册方法
def register(username,password):
#加密
res=get_md5(username,password)
#写入文件
with open('login',mode='at',encoding='utf-8') as f :
f.write(res)
f.write('\n')
#登录方法:
def login(username,password):
# 获取当前登录信息的加密结果
res = get_md5(username, password)
# 读文件,和其中的数据进行对比
with open('login', mode='rt', encoding='utf-8') as f:
for line in f:
if res == line.strip():
return True
else:
return False
while True:
op = int(input("1.注册 2.登录 3.退出 ----请输入:"))
if op == 3:
break
elif op == 1 :
username=input("请输入用户名:")
password=input("请输入密码:")
register(username,password)
elif op == 2 :
username=input("请输入用户名:")
password=input("请输入密码:")
res=login(username,password)
if res:
print('登录成功')
else:
print('登录失败')
出处:https://www.cnblogs.com/wushaofan/p/17156647.html