今日习题
# 1.什么是闭包函数
# 2.什么是作用域,查找顺序和加载顺序
# 3.什么是名称空间。查找顺序和加载顺序
# 4.如何局部修改全局可变数据类型和全局不可变数据类型
# 5.如何在局部的局部修改可变和不可变数据类型
昨日内容回顾
# 【一】函数的参数
# 【1】分为两大类
# 形参 : 定义在函数名后面的变量名
# 实参 : 实际调用函数的时候传递进去的指定值
# 【2】位置参数和关键字参数 : 按照传值方式的不同来区分
# 位置参数:按照位置传递参数 add(1,2)
# 关键字参数 : 按照关键字传递参数 add(x=1,y=2)
# 【3】可变长位置参数和可变长关键字参数
# 可变长位置参数:*args 定义在函数名后面的参数 可以接受多余的位置参数
# 可变长关键字参数:**kwargs 定义在函数名后面的参数 可以接受多余的关键字参数
# 【4】默认参数
# 在函数定义的时候,函数名后面的参数有默认值的形参叫默认参数
# 在函数调用的时候有值传入就用传入的值 如果没有值传入就用函数定义的默认值
# 【5】命名关键字参数
# 在函数定义阶段限制 参数只能按照位置传递 add(x,y,*,z,e)
# 在调用函数 add 的时候 x,y可以按照位置传也可以按照关键字传 , z 和 e 只能按照关键字传
# 【二】函数的类型注释
# 在函数定义阶段注释当前参数的类型
# 这是一个弱约束 ---> 不遵守不会报错 但是会有警告
# 强约束 ---> 不遵守就会直接抛出异常
# from typing import List, Tuple, Dict, Union,Optional
# 【三】名称空间
# 用来存放变量名和变量值映射关系的地方
# 内建名称空间 Python解释器自带的名称空间 关键字 def / if / else / class ...
# 局部名称空间 定义在函数或者类内部的名称空间 函数内部的变量名
# 全局名称空间 定义在Python文件里面的所有变量名和函数名以及类名
# 加载顺序:内建 -- 全局 -- 局部
# 查找顺序: 局部 -- 全局 -- 内建
# 【四】作用域
# 用来约束当前变量的作用区域 限制名称空间的查找范围
# LEGB
# L: Local 嵌套作用域
# E: Enclosing 局部作用域
# G: Global 全局作用域
# B: Built-in 内建作用域
# 嵌套作用域 定义在函数的函数内部的作用域
# 局部作用域 定义在函数内部的作用域
# 全局作用域 定义在函数外部的所有作用域
# 内建作用域 Python解释器自带的作用域
# 查找顺序 : 内嵌 -- 局部 -- 全局 -- 内建
# 加载顺序 : 内建 -- 全局 -- 局部 -- 内嵌
# 【五】局部修改全局不可变数据类型
# 局部修改全局可变数据类型 ---> 不需要任何处理 ---> 可变数据类型是内存地址不会改变
# 不可变数据类型 给一个不可变数据类型重新赋值
'''
age = 18
print(age, id(age)) # 18 4341400336
def change_age():
age = 28
print('change_age', age, id(age)) # change_age 28 4341400656
change_age()
print(age, id(age)) # 18 4341400336
'''
'''
age = 18
print(age, id(age)) # 18 4366205712
def change_age():
global age
age = 28
print('change_age', age, id(age)) # change_age 28 4366206032
change_age()
print(age, id(age)) # 28 4366206032
'''
'''
age = [18]
print(age, id(age)) # [18] 4301206720
def change_age():
age.append(28)
print('change_age', age, id(age)) # change_age [18, 28] 4301206720
change_age()
print(age, id(age)) # [18, 28] 4301206720
'''
# 【六】局部的局部 修改全局不可变数据类型
'''
age = 18
print(age, id(age)) # 18 4362765072
def change_age():
def inner():
age = 28
print('inner', age, id(age)) # inner 28 4362765392
inner()
change_age()
print(age, id(age)) # 18 4362765072
'''
'''
age = 18
print(age, id(age)) # 18 4297392912
def change_age():
def inner():
global age
age = 28
print('inner', age, id(age)) # inner 28 4297393232
inner()
change_age()
print(age, id(age)) # 28 4297393232
'''
'''
age = 38
print(age, id(age))
def change_age():
age = 18
print(age, id(age)) # 18 4328620816
def inner():
global age
print('inner global',age, id(age)) # inner global 38 4375692688
age = 28
print('inner', age, id(age)) # inner 28 4328621136
inner()
change_age()
print(age, id(age)) # 28 4328621136
'''
'''
age = 38
print(age, id(age))
def change_age():
age = 18
print(age, id(age)) # 18 4341629712
def inner():
nonlocal age
print('inner global',age, id(age)) # inner global 18 4341629712
age = 28
print('inner', age, id(age)) # inner 28 4328621136
inner()
change_age()
print(age, id(age)) # 28 4328621136
'''
# 【六】小结 global 和 nonlocal
# global 声明全局变量 加载到的是全局的不可变数据类型 +
# nonlocal 声明嵌套函数的局部变量 加载到的是嵌套函数的局部变量
今日内容详细
【一】闭包函数
# 【一】函数的调用方式
# 【1】函数可以被引用
'''
def add(x,y):
return x + y
res = add
res(1,2)
'''
# 【2】函数可以被作为容器类型的元素
'''
def add(x,y):
return x + y
func_dict = {
"add":add
}
'''
# 【3】函数作为参数传递给另一个函数使用
'''
def add(x, y):
return x + y
def pdd(x, y, func):
return func(x, y) + x + y
print(pdd(x=1, y=4, func=add))
'''
# 【4】函数可以作为函数的返回值
'''
def add(x, y):
return x + y
def pdd(x, y, func):
return func(x, y) + x + y
def odd():
# 函数在定义的时候其实是开辟了一块内存空间 存放函数的代码体
# 将这个函数的内存地址赋值给函数名 再通过函数名进行调用
return pdd
print(odd())
'''
# 【二】闭包函数
# 闭包函数:函数内部再定义一个函数,并且这个函数用到了外边函数的变量,
# 函数的返回值是可以是任意的值 包括函数本身的内存地址
# 作用域是定义完函数之后就已经被确定的关系
'''
def add():
age = 18
print(locals()) # {'age': 18}
def inner():
print(age)
print(locals()) # {'age': 18}
inner()
add()
print(globals())
# {'__name__': '__main__',
# '__doc__': '\ndef add(x,y):\n return x + y\n\nres = add\nres(1,2)\n',
# '__package__': None,
# '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x100db4d60>,
# '__spec__': None, '__annotations__': {},
# '__builtins__': <module 'builtins' (built-in)>,
# '__file__': '/Users/dream/Documents/PythonProjects/Python30/day12/03闭包函数.py',
# '__cached__': None,
# 'add': <function add at 0x100cf3f40>}
print(1)
add()
'''
# 【三】闭包函数的应用场景
# 【1】保持状态 可以将一个函数传给另一个参数 也可以将另一函数的返回值放回出来
'''
import requests
# 将值以参数的形式传入
def get(url):
return requests.get(url).text
# 方式一下载同一页面
get('https://www.python.org')
get('https://www.python.org')
get('https://www.python.org')
'''
'''
import requests
# 将值包给函数
def page(url):
# url = 'https://www.python.org'
print(url) # https://www.python.org
print(locals()) # {'url': 'https://www.python.org'}
def get():
print('get', url) # get https://www.python.org
print('get', locals()) # get {'url': 'https://www.python.org'}
return requests.get(url).text
return get
# 方式二下载同一页面
python = page('https://www.python.org')
print(python) # <function page.<locals>.get at 0x117c991b0>
python()
python()
# python()
'''
# 【2】装饰器
# 在不改变源代码和原调用方式的基础上进行增加额外的新功能
【二】装饰器
# 【一】什么是装饰器
# ● 装饰 代指为被装饰对象添加新的功能,器 代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。
# ● 概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
# ● 装饰器经常用于有切面需求的场景
# ○ 插入日志、性能测试、事务处理、缓存、权限校验等应用场景
# ○ 装饰器是解决这类问题的绝佳设计
# ○ 有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
# 提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。
# 【二】装饰器的用途
# ● 软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。
# ○ 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
# ○ 对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
# ● 软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃
# ○ 而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。
# 【三】装饰器的分类
# ● 函数装饰器分为:无参装饰器和有参装饰两种
# ● 二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
【三】无参装饰器
【1】引入
import time
# 【一】无参装饰器
# 给一个函数增加一个时间计数器
# print(time.time()) # 1722305790.024003
# 时间戳,是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数(不考虑闰秒),用于表示一个时间点。
# 然而,这种格式对于人类阅读并不友好,因此需要转换成可读的日期和时间格式。
# 这个工具能够将时间戳快速转换为人类可读的日期时间格式,同时也支持反向转换,即将日期时间转换为时间戳。
'''
start_time = time.time()
time.sleep(2)
end_time = time.time()
print(end_time - start_time) # 2s
'''
def index():
start_time = time.time()
time.sleep(2)
end_time = time.time()
print(f"总耗时 :>>>> {end_time - start_time}s")
index()
def func():
start_time = time.time()
time.sleep(2)
end_time = time.time()
print(f"总耗时 :>>>> {end_time - start_time}s")
# 上面的方式是在每一个函数的内部计算耗时,但是问题是 每一次都要写一个计时代码
def index():
print('index 运行')
time.sleep(2)
def func():
print('func 运行')
time.sleep(2)
start_time = time.time()
# index()
func()
end_time = time.time()
print(f"总耗时 :>>>> {end_time - start_time}s")
# 问题:每一次都要将上一个函数注释掉然后启动新的函数去计算耗时
def index():
print('index 运行')
time.sleep(2)
return 'index 运行结束'
def home():
print('func 运行')
time.sleep(2)
return 'func 运行结束'
def timer(func):
print(func) # <function index at 0x1031ee050>
start_time = time.time()
res = func() # 获取到传入进来的函数的内存地址 ---> 执行函数
end_time = time.time()
print(f"总耗时 :>>>> {end_time - start_time}s")
return res
res = timer(func=index)
print(res)
res = timer(func=home)
print(res)
def home():
print('func 运行')
time.sleep(2)
return 'func 运行结束'
def index():
print('index 运行')
time.sleep(2)
return 'index 运行结束'
def timer(func):
# print(func) # <function index at 0x1031ee050>
def inner():
start_time = time.time()
res = func() # 获取到传入进来的函数的内存地址 ---> 执行函数
end_time = time.time()
print(f"总耗时 :>>>> {end_time - start_time}s")
return res
return inner
print(index) # <function index at 0x1232451b0>
index = timer(func=index)
print(index)
print(index())
home = timer(func=home)
print(home)
print(home())
def index():
print(f"index starting .... ")
time.sleep(2)
return 'index 运行结束'
# 想给原本的函数 index 加上一个 计时功能并且不要改变原来的调用方式
# index()
def timer(func):
# func ---> index 真正的函数内存地址
def inner():
result = func()
# 原本的 index 函数 可能会有返回值
return result
return inner
# timer() ---> 返回值是 inner 的函数地址 ---> 重命名函数地址
index = timer(func=index) # index 就是 timer 内的 inner 的内存地址
result = index() # index() ---> timer 内的 inner 执行 ---> func() ---> index 真正的函数内存地址 ---> index()
# result 是谁? 是你真正的index 函数执行后返回的返回值
【2】练习
# 练习 :有一个取款的函数
def withdraw():
print(f"取款 starting .... ")
time.sleep(2)
return True, '取款 运行结束'
def insert():
print(f"insert starting .... ")
time.sleep(2)
return True, 'insert 运行结束'
# 验证当前用户登录过后才能进行取款
# 如果没有登陆过要让用户去登录
# 并且 不要改变原本的 取款的调用方式
# print(withdraw())
login_dict = {
"username": 'None'
}
def login_auth(func):
# func ---> withdraw 真正的函数内存地址
def inner():
# 在进入 withdraw 功能之前 进行验证登陆
if login_dict.get("username"):
res = func()
return res
else:
return False, '请先登陆!'
return inner
withdraw = login_auth(func=withdraw)
res = withdraw()
print(res)
insert = login_auth(func=insert)
res = insert()
print(res)
# 登陆认证
# 取款
# 验证用户名 验证密码
# 先验证用户名 再验证密码
def withdraw():
print(f"取款 starting .... ")
time.sleep(2)
return True, '取款 运行结束'
login_dict = {
"username": 'dream',
"password": "521"
}
def login_auth(func):
# func ---> withdraw 真正的函数内存地址
def inner():
# 在进入 withdraw 功能之前 进行验证登陆
if login_dict.get("username") != "dream":
return False, f'用户名错误!'
elif login_dict.get("password") != "521":
return False, '密码错误!'
result = func()
return result
return inner
withdraw = login_auth(withdraw)
print(withdraw())
【3】无参装饰器的模板
def outer(func):
'''
:param func:真正的函数内存地址
:return:
'''
def inner():
# 在真正的进入到 上面的 func 之前进行认证
# 执行真正的函数
result = func()
# 处理执行后的结果
# 对返回结果进行定制化返回
return result
return inner
【四】有参装饰器
【1】引入
login_dict = {
"username": "dream",
"password": "521"
}
def withdraw(username, money):
print(f"当前用户{username}正在提现{money}元")
def transform(username, to_username, money):
print(f"当前用户{username} 给 {to_username} 转账了 {money}元")
# withdraw(username="dream", money="521")
def login_auth(func):
def inner(username, to_username, money):
if login_dict.get("username"):
return func(username, money) # withdraw(参数)
else:
return False, f'当前请先登陆!'
return inner
withdraw = login_auth(withdraw)
res = withdraw(username="dream", money=999) # withdraw --> login_auth 内 的 inner ---> inner() ---> func() ---> withdraw()
print(res)
# withdraw() missing 2 required positional arguments: 'username' and 'money'
# 报错:原因是原本函数的调用需要传入两个位置参数 但是这里调用的时候没有传入
【2】解决办法
# 有几个传几个 但是不兼容所有函数
def login_auth(func):
def inner(username, money):
if login_dict.get("username"):
return func(username, money) # withdraw(参数)
else:
return False, f'当前请先登陆!'
return inner
withdraw = login_auth(withdraw)
# withdraw(username, money)
res = withdraw(username="dream",money=999) # withdraw --> login_auth 内 的 inner ---> inner() ---> func() ---> withdraw()
print(res)
# 可变长参数 兼容所有函数的参数
def login_auth(func):
def inner(*args, **kwargs):
print(args) # ('dream', 'opp', 888)
print(kwargs) # {'username': 'dream', 'money': 999}
if login_dict.get("username"):
return func(*args, **kwargs) # withdraw(参数)
else:
return False, f'当前请先登陆!'
return inner
withdraw = login_auth(withdraw)
# withdraw(username, money)
res = withdraw(username="dream",
money=999) # withdraw --> login_auth 内 的 inner ---> inner() ---> func() ---> withdraw()
print(res)
transform = login_auth(transform)
# transform(username, to_username, money)
res = transform("dream", "opp", 888)
【3】有参装饰器模板
def outer(func):
def inner(*args, **kwargs):
# 第一部分 在正式进入func 之前进行校验
# 第二部分 正式执行 原本的函数
result = func(*args, **kwargs)
# 第三部分 对返回的结果进行处理然后返回
return result
return inner
【五】语法糖
【1】有参装饰器
def withdraw(username, money):
return f"当前 {username} 取款 {money} 元!"
def transform(username, to_username, money):
return f'当前 {username} 给 {to_username}转账 {money} 元!'
def login_auth(func):
def inner(*args, **kwargs):
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
# login_auth(withdraw) 返回值是 login_auth 里面的 inner 函数的内存地址
# 给 login_auth 里面的 inner 函数的内存地址 重新赋值 withdraw
withdraw = login_auth(withdraw)
# withdraw() ---> login_auth 里面的 inner() ---> inner 里面的func() ---> withdraw() ---> 原本真正的函数()
withdraw("dream", 1000)
【2】语法糖
# Python开发者为了更加方便的给当前函数加上指定的装饰器 于是就有了一种新语法
# 语法糖 : 语法糖就是一种便捷的语法 本质还是原来的语法
login_dict = {
"username": "dream",
"password": "521"
}
def login_auth(func):
def inner(*args, **kwargs):
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
@login_auth # 相当于 withdraw = login_auth(withdraw)
def withdraw(username, money):
return f"当前 {username} 取款 {money} 元!"
print(withdraw("dream",521))
@login_auth
def transform(username, to_username, money):
return f'当前 {username} 给 {to_username}转账 {money} 元!'
print(transform("dream","hope",521))
【六】多层语法糖
【1】多层有参装饰器
login_dict = {
"username": "dream",
"role": "admin"
}
def login_decorator(func):
# func ---> withdraw 函数的内存地址
def inner(*args, **kwargs):
print("login_decorator")
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
def permission_decorator(func):
# func ---> login_decorator 的 inner 函数地址
def inner(*args, **kwargs):
print("permission_decorator")
if login_dict.get("role") != "admin":
return "当前没有权限访问当前功能!"
return func(*args, **kwargs)
return inner
# 必须是登录状态且是管理员权限的情况下才能取款
# withdraw = login_decorator(withdraw) 的返回值 = login_decorator 的 inner 函数地址
withdraw = login_decorator(withdraw)
# withdraw = permission_decorator(login_decorator 的 inner 函数地址)的返回值 = permission_decorator 的 inner 函数地址
withdraw = permission_decorator(withdraw)
# permission_decorator 的 inner 函数
withdraw("Dream", "9999")
【2】多层语法糖
login_dict = {
"username": "dream",
"role": "admin"
}
def login_decorator(func):
# func ---> withdraw 函数的内存地址
def inner(*args, **kwargs):
print("login_decorator")
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
def permission_decorator(func):
# func ---> login_decorator 的 inner 函数地址
def inner(*args, **kwargs):
print("permission_decorator")
if login_dict.get("role") != "admin":
return "当前没有权限访问当前功能!"
return func(*args, **kwargs)
return inner
# 多层语法糖的包装顺序是从下向上包装
# 执行顺序是从上向下执行
@login_decorator # withdraw = login_decorator(permission_decorator 里面的 inner) = login_decorator 里面的 inner 函数地址
@permission_decorator # withdraw = permission_decorator(withdraw真正的) = permission_decorator 里面的 inner
def withdraw(username, money):
return f"当前 {username} 取款 {money} 元!"
# 这里的 withdraw 函数的内存地址是 login_decorator 里面的 inner 函数地址
res = withdraw("Dream", "9999")
print(res)
【七】有参语法糖
【1】有参语法糖前身
login_dict = {
"username": "dream",
"role": "admin"
}
def decorator(tag):
# print(tag) # <function withdraw at 0x1023c6050>
if tag == "login":
def login_decorator(func):
# func ---> withdraw 函数的内存地址
def inner(*args, **kwargs):
print("login_decorator")
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
return login_decorator
elif tag == "permission":
def permission_decorator(func):
# func ---> login_decorator 的 inner 函数地址
def inner(*args, **kwargs):
print("permission_decorator")
if login_dict.get("role") != "admin":
return "当前没有权限访问当前功能!"
return func(*args, **kwargs)
return inner
return permission_decorator
# withdraw = login_decorator(withdraw) 的返回值 = login_decorator 的 inner 函数地址
login_auth = decorator(tag="login")
permission = decorator(tag="permission")
# <function decorator.<locals>.login_decorator at 0x117f111b0>
withdraw = permission(withdraw) # withdraw = permission_decorator(withdraw)
withdraw = login_auth(withdraw) # withdraw = login_decorator(withdraw)
print(withdraw("Dream", 999))
【2】有参语法糖真身
login_dict = {
"username": "dream",
"role": "admin"
}
def decorator(tag):
# print(tag) # <function withdraw at 0x1023c6050>
if tag == "login":
def login_decorator(func):
# func ---> withdraw 函数的内存地址
def inner(*args, **kwargs):
print("login_decorator")
if not login_dict.get("username"):
return "当前未登录请先登陆!"
return func(*args, **kwargs)
return inner
return login_decorator
elif tag == "permission":
def permission_decorator(func):
# func ---> login_decorator 的 inner 函数地址
def inner(*args, **kwargs):
print("permission_decorator")
if login_dict.get("role") != "admin":
return "当前没有权限访问当前功能!"
return func(*args, **kwargs)
return inner
return permission_decorator
@decorator(tag='login') # @login_decorator
@decorator(tag='permission') # @permission_decorator
def withdraw(username, money):
return f"当前 {username} 取款 {money} 元!"
print(withdraw("Dream", 999))