一、装饰器
装饰器的逐渐演变过程
1.1 闭包
def foo(func):
def index():
print('top')
func()
print('bottom')
return index
1.2 可变长度参数
*args, **kwargs
:在形参中表示可接受任意多个,任意形式的参数
*名字
:在作为形参时,表示可接受任意多个位置参数*名字
:在作为实参时,表示炸开为多个位置参数**名字
:在作为形参时,表示可接受任意多个关键字参数**名字
:在作为实参时,表示炸开为多个关键字参数
def w(*args, **kwargs):
print(args) # 一个元组,传递进来的位置参数
print(kwargs) # 一个字典,传递进来的关键字参数
# 例
w()
w(1)
w(1, 2)
w(1, name='哒卟了')
x = [1, 2, 3]
y = {'name': '哒卟了', 'age': 18}
w(*x)
w(**y)
""" 结论:
()
{}
(1,)
{}
(1, 2)
{}
(1,)
{'name': '哒卟了'}
(1, 2, 3)
{}
()
{'name': '哒卟了', 'age': 18}
"""
1.3 装饰器模板⭐⭐⭐
def foo(func):
def index(*args, **kwargs): # 将函数名作为参数传递进来,实现动态变化
# 先新增功能,再执行原功能
print('先增功能')
res = func(*args, **kwargs) # 原函数实现的功能
# 先执行原功能,再新增功能
print('后增功能')
return res
return index
# 语法糖:需要哪个函数新增该装饰器功能,只要在该函数上一行 @装饰器名字
# 案例:登录后,观看芒台的综艺、电视剧、电影、动漫等
def show(types):
def operation(*args, **kwargs):
if login: # 要求登录后方可观看
res = types(*args, **kwargs)
return res
return operation
@show
def variety(): # 综艺
print("I like variety~ ")
@show
def teleplay(): # 电视剧
print("I'm watching teleplay. ")
@show
def movie(name): # 电影
print(f"The movie {name} is very good! ")
while True:
is_login = input("是否登录(是1,否2):").strip()
if is_login == '1':
login = True
option = input("请选择 1综艺,2电视剧,3电影,4退出:").strip()
while option != '4':
if option == '1':
variety()
elif option == '2':
teleplay()
elif option == '3':
film = input("请输入电影名称:").strip()
movie(film)
option = input("请选择 1综艺,2电视剧,3电影,4退出:").strip()
else:
login = False
print("请您先登录")
""" 结果:
是否登录(是1,否2):1
请选择 1综艺,2电视剧,3电影,4退出:1
I like variety~
请选择 1综艺,2电视剧,3电影,4退出:2
I'm watching teleplay.
请选择 1综艺,2电视剧,3电影,4退出:3
请输入电影名称:Garfield
The movie Garfield is very good!
请选择 1综艺,2电视剧,3电影,4退出:4
是否登录(是1,否2):2
请您先登录
"""
二、函数进阶
2.1 迭代器、可迭代对象、生成器
迭代器
- 迭代取值的工具,涉及可取多值,可循环取出数据(
str, list, dict, tuple, set, file
) - 内置
__next__()
方法:逐一取出内容,取完后再取会报错(如母鸡下蛋) - 内置
__iter__()
方法:迭代器 – 经该方法后 --> 迭代器
可迭代对象
- 可转化为迭代器的对象
str, list, dict, tuple, set, file
- 内置
__iter__()
方法 :可迭代对象 – 经该方法后 --> 迭代器
# 案列
str_a = '123'
list_a = [1, 2, 3]
dict_a = {'name': '哒卟了', 'age': 18}
tuple_a = (1, 2, 3)
set_a = {1, 22, 3}
# 内置 __iter__ 方法
print(str_a, id(str_a)) # 字符串, 内存地址 123 1660694400752
b = iter(str_a) # 可迭代对象 --> 迭代器
print(b, id(b)) # 迭代器 内存地址 <str_iterator object at 0x00000182AADA3308> 1660723802888
c = iter(b) # 迭代器 --> 迭代器
print(c, id(c)) # 迭代器 内存地址(同b)<str_iterator object at 0x00000182AADA3308> 1660723802888
# 内置 __next__ 方法
print(next(b)) # 1
print(next(b)) # 2
生成器
-
自定义的迭代器
-
一旦函数内部出现
yield
关键字,这个函数就会变成 生成器,执行该函数得到 一个迭代器(即得到地址,不会执行函数内部代码) -
该类函数通常与for循环结合使用,for循环可依次获取 yield 后的数据
-
for 变量 in 生成器函数名(): 代码 # 不加()是名字,内存地址 # 加了()是执行函数,得到一个迭代器
-
# 案例 def test_a(): print(1) yield 11 print(2) yield 22 print(test_a) print(test_a()) for x in test_a(): print(x) """ 结果: <function test_a at 0x00000141F6CB5678> <generator object test_a at 0x00000141F6C386C8> 1 11 2 22 """
-
-
扩展
send
(了解): 生成器中yield 为变量,不能定值-
def test_b(): print(1) received = yield yield received print(2) received2 = yield yield received2 gen = test_b() gen.send(None) # next(gen) 启动生成器 print(gen.send('new data')) # 发送数据到生成器,输出1 new data next(gen) # gen.send(None) 再次启动等待接收状态 print(gen.send('new data2')) # 发送数据到生成器,输出 2 new data2
-
-
yield
与return
区别:- return 函数一旦执行到它,就会立即退出
- yield 每执行一次
next()
就会执行函数内部的代码,直到遇到yield
暂停在那里,等下一次next()
时,继续执行代码,将yield
后的数据求出去,然后继续执行至下一个yield
2.2 迭代循环
for 循环可称之为迭代循环,for 循环的本质原理是基于迭代器的next()方法
for 变量 in iter(可迭代对象, 迭代器):
# 会依次调用iter()
- 1)
d.__iter__()
得到一个迭代器对象 - 2)
迭代器对象.__next__()
拿到一个返回值,然后将该返回值赋值给k - 3)循环往复步骤2,直到抛出异常,for循环会捕捉异常然后结束循环
三、模块与包
模块:一组功能的集合,多个功能的结合。一份python文件就是一个模块
模块的作用:提升效率,方便管理
3.1 模块的分类
内置模块 < 自定义模块 < 第三方模块 (最多)
3.2 自定义模块的使用
导入模块时,做的几件事:
- 导入模块时 会执行那个模块里面的代码
- 产生一个和模块名字相同的名称空间,将那个模块里产生的名字丢到那个名称空间去
- 我们需要使用那个模块里的名字时,通过
模块.名字
即可使用
# 导入单个模块(推荐)
import 模块名字/py文件名 # 调用 模块名.名字
from 模块名 import 名字 # 可直接使用名字调用
import 模块名 as 别名
from 模块名 import *
"""
当模块里有个名叫 __all__ 的列表时,就只能导入这个列表里的字符串元素的名字;
若没有 __all__ 时,不能导入以 下划线开头的名字,其他名字可直接使用。
很容易产生名字冲突的情况,所以一般不使用
"""
# 导入多个模块
import 模块名1, 模块名2
"""书写顺序规范:
内置模块
第三方模块
自定义模块
"""
导入位置区别:
- 在外面导入:全局的,任意地方都可使用
- 在函数内导入:局部的,只能在函数内部使用
📌tips:运行一半报错,因为python是解释型语言,即执行到哪算哪,哪里错了就报错。
3.3 循环导入问题(设计代码阶段)
问题描述:
# a.py
print('正在导入a')
from b import y
x = 'aa'
# b.py
print('正在导入b')
from a import x
y = 'bb'
# test.py
import a
"""运行结果:
正在导入a
正在导入b
ERROR报错....
"""
正确解决方案:
-
一开始编写代码时应避免循环导入问题
-
加入第三方,多个模块需要用到的名字,就放在一份文件中,作为公用
3.4 搜索模块的路径
模块查找问题:
- 先查找内存,从
sys.path
这个列表中的元素文件夹内部去找模块,按先后顺序来(第一个元素即当前文件夹 动态变化) - 都找不到就报错。若必须导入某模块,但路径不在
sys.path
的列表中,可通过 先在 sys 中添加路径的方式,即:sys.path.append('该模块路径')
,再调用该模块 - 第三方模块 下载存放在python文件夹下的
lib\site-packages
中
报错问题:
Module Not Found
:模块找不到Name Not defined
: 名字没定义SyntaxError
:尖括号在下方,表语法错误
⭐ __name__
在Python文件执行时,结果永远是 __main__
,换言之,若 __name__ == '__main__'
,则表示正在执行该文件;
def run():
print('我要开始运行这个功能啦')
if __name__ == '__main__':
# 此处代码一般作为程序的启动入口,只有直接运行该文件时才会执行
run()
但导入该文件时,结果是该文件的 模块名
3.5 包
包 本质上还是一个模块,一个放着python的模块文件的文件夹叫包
重复执行一段代码 -----> 循环
重复使用一个功能 -----> 函数
多个功能的集合 -----> 模块 -----> 文件
多个模块的集合 -----> 包 -----> 文件夹
📌tips:python2 版本中文件夹里必须有
__init__.py
文件才可称之为包;但python3版本中没有该限制,有无都可,约定会放该文件
3.5.1 创建包
方法一:自己新建文件夹,在文件夹中创建python文件
方法二:利用 pycharm 创建,鼠标右击 NEW —> python Package (会自动创建 __init.py
文件)
3.5.2 导入包
# 1.导入
import 包的名字(文件夹的名字) # 导入包后会执行 __init__.py 中的代码,其他文件代码不执行
import 包名.模块名 # 调用:包名.模块名.名字
from 包名 import 模块名 # 调用:模块名.名字
import 包名1.模块名, 包名2.模块名
from 包名 import 模块名1, 模块名2
# 2.取别名
import 包名.模块名 as 别名 # 调用:别名.名字
from 包名 import 模块名 as 别名 # 调用:别名.名字
import 包名.模块名 as 别名, 包名.模块名 as 别名
from 包名.xxx import yyy as zzz # 推荐
from . import xxx
"""
a
b.py
c.py
from .b import name # 导入a文件夹下的b.py中的name
# 直接运行该文件会报错
index.py
from a import c # 不会报错
"""
3.6 第三方模块(第三方库/包)
下载别人写好的文件,放在 sys.path
的列表中,即 ..\Python39\Lib\site-packages
-
下载第三方库,在
cmd
中执行该命令即可自动下载-
pip install 第三方库名
第三方官方网站:pypi.python.org/
-
下载很慢问题:因为官网是国外的,所以可用国内镜像网站(换源)
-
临时换源:(下载速度快,但每次下载都要加 源址)
- 豆瓣:
pip install 第三方库名字 -i https://pypi.douban.com/simple
- 阿里云:
pip install 第三方库名字 -i https://mirrors.aliyun.com/simple
- 豆瓣:
-
永久换源:(下载快,且不用每次都加 源址)
-
本质是更换pip的网址
-
""" 1. 文件管理器文件路径地址栏敲:%APPDATA% 回车,快速进入 C:\Users\电脑用户\AppData\Roaming 文件夹中 2. 新建 pip 文件夹并在文件夹中新建 pip.ini 配置文件 3. 新增 pip.ini 配置文件内容 """ # 配置文件内容 [global] index-url = http://pypi.douban.com/simple [install] use-mirrors = true mirrors = http://pypi.douban.com/simple/ trusted-host = pypi.douban.com
-
-
-
pip 安装第三方库报错
- 最简单粗暴的方式:重装python
- 复杂点儿的方式: 将
D:\Tool\python\Scripts
加入环境变量即可
-
-
卸载第三方库:
pip uninstall 第三方库名字
四、常用的内置模块
4.1 和时间相关
time.time()
time.sleep(a)
import time
print(time.time()) # 得到一个时间戳,此时此刻距离 1970.1.1.00:00:00 的秒数
# 主要用于计算时间差
a = time.time()
time.sleep(1) # 让程序休息几秒钟,再执行下面的代码
b = time.time()
time_stamp = b - a
print(f"时间戳:{time_stamp}秒")
"""结果:
1725440838.9463332
时间戳:1.0119984149932861秒
"""
4.2 随机数
random.random()
random.randint(a, b)
import random
print(random.random()) # (0,1) 浮点数
print(random.randint(1, 3)) # [1,3] 整数
print(random.randrange(1, 3)) # [1,3) 整数
"""结果:
0.5068625100520493
3
2
"""
# 案例:随机验证码 --> 6个随机码 0~9 a~Z
# tips:A == chr(65) 查ASCII表 65代表的字符
import random
res = ''
for i in range(6):
str1 = chr(random.randint(65, 90)) # A-Z(65-90)
str2 = chr(random.randint(97, 122)) # a-z(97-122)
str3 = str(random.randint(0, 9)) # 0-9
res += random.choice([str1, str2, str3])
print("验证码:", res)
"""结果:
验证码: 6CrnTI
"""
4.3 os模块
if not os.path.exists():
os.mkdir()
os.path.join()
import os
os.mkdir('新建一个文件夹') # 创建一个文件夹,若该文件夹已存在,则报错
res = os.path.exists('../folder') # 判断一个路径是否存在,存在True,不存在False
print(res) # True 表存在该路径
if not os.path.exists('新建文件夹'): # 经常结合使用:若无该文件夹则新建
os.mkdir('新建文件夹')
res = os.path.join('code', '新建文件夹', 'test.py') # 拼接形成适配操作系统格式的文件路径
print(res) # code\新建文件夹\test.py
4.4 sys模块
sys.path
import sys
sys.path.append('路径') # 将某个路径加入sys.path 可避免模块找不到的问题
4.5 json
json
用于解决跨语言、跨平台的问题 == 相当于 ==> 不同地区各有方言,但大家可通过普通话沟通
json.dumps()
json.dump()
json.loads()
json.load()
import json
# json.dumps(python的数据) 将python数据转成json数据,集合类型不能转成json
a = {'name': '哒卟了', 'age': 18} # <class 'dict'>
print(type(a))
b = json.dumps(a) # <class 'str'>
print(b) # {"name": "\u54d2\u535f\u4e86", "age": 18}
print(type(b))
# json.loads(json的数据) 将json数据转成python数据
c = json.loads(b)
print(c) # {'name': '哒卟了', 'age': 18}
print(type(c))
json
文件 .json
里面存放的是json
数据类型
data = {'name': '哒卟了', 'age': 18}
# 写入json文件
with open('test.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(data)) # 先将python数据转成json数据,再写入
# 读取json文件
with open('test.json', 'r', encoding='utf-8') as f:
data = f.read() # 此时读出的为json数据
data = json.loads(data) # 将json数据转为python数据
print(data, data['name']) # {'name': '哒卟了', 'age': 18} 哒卟了
# 简便写法 (无s, 仅限于文件中)
# with open() as 文件对象:
# json.dump(python数据, 打开的文件对象)
with open('test.json', 'w', encoding='utf-8') as f:
json.dump(data, f)
# with open() as 文件对象:
# data = json.load(打开的文件对象)
with open('test.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(data)
4.6 加密模块 hash哈希内置模块
hash特点:
- 只要传入的内容一样,得到的hash值必然一样
- 应用场景:验证文件的完整性,加密思想
- 不能由hash值反解出内容
- 应用场景:密码加密
- 只要使用的hash算法不变,无论校验内容有多大,得到的hash值长度是固定的
- 内部算法实现
import hashlib
# 1. 选择一个算法,常用md5算法,其他算法:sha1 -- sha256 -- sha512 (运算难度更大,对CPU要求更大)
m = hashlib.md5()
# 2. 使用算法对数据进行加密,数据必须是二进制,字符串要先编码
m.update('哒卟了'.encode('utf8'))
# 3. 得到加密后的结果
b = m.hexdigest()
print(b, "\t长度:", len(b), "位数字") # 7b1349528e92a0615422502bd58381ce 长度: 32 位数字
⭐⭐⭐加密必加盐:增加破译难度,提高破译成本
import hashlib
# 方法一:先加盐再加密
pwd = '678678' # 密码
salt = 'I am 哒卟了' # 盐
new_pwd = pwd + salt # 加盐
m = hashlib.md5()
m.update(new_pwd.encode('utf8')) # 加密
b = m.hexdigest()
print(b, "\t长度:", len(b), "位数字") # 3b0ebc595f58f3d111caea84011984c7 长度: 32 位数字
# 方法二:先加密再加盐
pwd = '678678' # 密码
salt = 'I am 哒卟了' # 盐
m = hashlib.md5()
m.update(pwd.encode('utf8')) # 加密
m.update(salt.encode('utf8')) # 加盐
b = m.hexdigest()
print(b, "\t长度:", len(b), "位数字") # 3b0ebc595f58f3d111caea84011984c7 长度: 32 位数字
📌总结:
登录、注册等 涉及到安全的一定要加密
注册时将密码加密(要加盐值),将最后结果保存
登录时将密码加密(要加盐值),将最后结果与保存结果进行比对
只需对密码进行加密,账户不需要