Python 闭包函数和装饰器

【一】闭包函数

【1】定义

  • 闭包是指在函数内部创建一个独立的作用域,该作用域中的变量和函数可以在函数外部被访问。
  • 简单来说:闭包就是函数及其相关引用环境组合而成的实体
# outer是闭包函数,inner是闭包,引用外层的作用于函数的变量
def outer(a):
    def inner():
        return a**2
    return inner

# outer不是闭包函数,inner也不是闭包,没有引用外层作用域的变量
b = 1
def outer():
    def inner():
        return b**2
    return inner

【2】特点和优点

(1)特点
  • 函数嵌套函数
  • 内部函数引用外部函数的变量
(2)优点
  • 代码简洁:闭包可以将相关的函数和变量组合在一起,使代码更加简洁和模块化。
  • 数据隐私:内部函数可以访问外部函数的私有变量,实现了数据的隐私保护。
  • 内存效率:闭包可以避免创建大量的临时变量,提高内存使用效率。

【3】例子

def outer_func(x):
    def inner_func(y):
        return x + y

    return inner_func

add_five = outer_func(5)   
print(add_five(3))
print(add_five(7))
# 8
# 12
def outer(x):
    def inner_reads():
        return x

    def inner_writes(y):
        x = y

    def inner_error(y):
        tmp = x      #定义在引用的下面, 报错
        x = y
        return tmp

    return inner_reads, inner_writes, inner_error


inner_reads, inner_writes, inner_error = outer(5)
print(inner_reads())
inner_writes(10)
print(inner_reads())
inner_error(10)
print(inner_reads())

【4】判断闭包的方法

  • 理论依据:是否引用外层函数作用域中的变量
  • 函数方法:使用函数closure属性,不是闭包该值为None
# func.__closure__		# 元组形式
# func.__closure__[0].cell_contents	 # 查看具体引用的第一个值

def outer(a):
    def inner():
        x = a
    print(f"{inner.__name__}是否是闭包函数:{inner.__closure__}")
    return True
outer(1)
print(f"{outer.__name__}是否是闭包函数:{outer.__closure__}")
# inner是否是闭包函数:(,)
# outer是否是闭包函数:None

a = 2
def outer():
    def inner():
        x = a
    print(f"{inner.__name__}是否是闭包函数:{inner.__closure__}")
    return True
outer()
print(f"{outer.__name__}是否是闭包函数:{outer.__closure__}")
# inner是否是闭包函数:None
# outer是否是闭包函数:None

【二】装饰器函数

【1】定义和作用

  • 装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
  • 装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

【2】普通函数到无参装饰器的转变(例:时间性能测试)

(1)函数作为参数
  • 将函数地址传入,返回的直接是执行结果
  • 返回值看着不便,希望直接是函数名
import time

def func1():
    time.sleep(1.1)
    print("func1开始运行")

def func2():
    time.sleep(1.2)
    print("func2开始运行")

def test_time(func):
    start_time = time.time()
    res = func()
    end_time = time.time()
    msg = f"{func}运行:{round(end_time - start_time, 2)}"
    return msg


res = test_time(func1)
print(res)
res = test_time(func2)
print(res)
# func1开始运行
# 运行:1.1
# func2开始运行
# 运行:1.2
(2) 将值给闭包函数
  • 可以使用同名变量接收函数
  • 返回地址后还需要添加括号运行
import time

def func1():
    time.sleep(1.1)
    print("func1开始运行")

def func2():
    time.sleep(1.2)
    print("func2开始运行")

def test_time(func):
    def inner():
        start_time = time.time()
        res = func()
        end_time = time.time()
        msg = f"{func}运行:{round(end_time - start_time, 2)}"
        return msg
    return inner

func1 = test_time(func1)
print(func1())
func2 = test_time(func2)
print(func2())
# func1开始运行
# 运行:1.1
# func2开始运行
# 运行:1.2
(3)语法糖
  • **(语法糖)**可以直接使用原函数名称
  • 装饰器函数需要定义在所有调用装饰器函数的最上方
import time

def test_time(func):
    def inner():
        start_time = time.time()
        res = func()
        end_time = time.time()
        msg = f"{func}运行:{round(end_time - start_time, 2)}"
        return msg
    return inner

@test_time				# 等价于 func1 = test_time(func1)
def func1():
    time.sleep(1.1)
    print("func1开始运行")

@test_time				# 等价于 func2 = test_time(func2)
def func2():
    time.sleep(1.2)
    print("func2开始运行")

print(func1())
print(func2())
# func1开始运行
# 运行:1.1
# func2开始运行
# 运行:1.2
(4)需要数据传递时(*args**kwargs
import time

def test_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        msg = f"{func}运行:{round(end_time - start_time, 2)}"
        return msg
    return inner

@test_time  # 等价于 func1 = test_time(func1)
def func1(*args, **kwargs):
    print("func1开始运行")
    time.sleep(1.1)
    str1 = args[0]
    name = kwargs.get("name")
    print(f"{name}, {str1}")


print(func1("hello, world", name="tom"))
# func1开始运行
# tom, hello, world
# 运行:1.1
(5)无参装饰器模板
def decorator(func):
    # 装饰器声明时操作
    def inner(*args, **kwargs):
        # 函数执行前操作
        res = func(*args, **kwargs)
        # 函数执行后操作
        return res
    return inner

【3】多层装饰器执行顺序

(1)代码讲解
def decorator1(func):
    print("装饰器1--被应用")
    def inner(*args, **kwargs):
        print("装饰器1--函数执行前操作")			# 3.
        res = func(*args, **kwargs)
        print("装饰器1--函数执行后操作")			# 7.
        return res
    return inner

def decorator2(func):
    print("装饰器2--被应用")
    def inner(*args, **kwargs):
        print("装饰器2--函数执行前操作")			# 4.
        res = func(*args, **kwargs)
        print("装饰器2--函数执行后操作")			# 6.
        return res
    return inner
	
@decorator1										# 2. 打印 装饰器1--被应用
@decorator2										# 1. 打印 装饰器2--被应用
def func1():
    print("函数--开始工作")						# 5.

func1()

# 装饰器2--被应用
# 装饰器1--被应用
# 装饰器1--函数执行前操作
# 装饰器2--函数执行前操作
# 函数--开始工作
# 装饰器2--函数执行后操作
# 装饰器1--函数执行后操作

(2)小练习
  • 登录校验装饰器
  • 密码校验装饰器
  • 有元素内容的传递
user_data = {"name": "tom", "psw": "000"}
bank_data = {"dream": {"pay_psw": "000", "balance": "1000"}}

def login_check(func):
    def inner(*args, **kwargs):
        name = input("请输入用户名>>>:").strip()
        psw = input("请输入登录密码>>>:").strip()
        if name != user_data["name"] or psw != user_data["psw"]:
            return False, "用户名或者密码错误"
        flag, msg = func(name=name)
        return flag, msg
    return inner

def pay_psw_check(func):
    def inner(*args, **kwargs):
        balance = input("请输入金额>>>:").strip()
        if not balance.isdigit() or int(balance) < 0 or int(balance) > int(bank_data["dream"]["balance"]):
            msg = f"输入金额{balance}存在问题,不是数字或者超出了你的余额"
            return False, msg
        pay_psw = input("请输入支付密码>>>:").strip()
        if pay_psw != bank_data["dream"]["pay_psw"]:
            return False, "支付密码错误"
        flag, msg = func(balance=balance, **kwargs)
        return flag, msg
    return inner

@login_check
@pay_psw_check
def get_balance(*args, **kwargs):
    name = kwargs.get("name")
    balance = kwargs.get("balance")
    msg = f"用户{name}取款{balance}成功"
    return True, msg

flag, msg = get_balance()
print(f"flag:{flag}-----msg:{msg}")

【6】有参装饰器

(1)代码讲解
  • 不使用语法糖
def repeat(n):
    def decorator(func):
        def inner(*args, **kwargs):
            print(f"函数{func.__name__}j将重复执行{n}遍")
            for _ in range(n):
                func(*args, **kwargs)
        return inner
    return decorator

def hello(name):
    print(f"Hello, {name}")

hello = repeat(2)(hello)
hello("tom")
# 函数helloj将重复执行2遍
# Hello, tom
# Hello, tom
  • 使用语法糖
def repeat(n):
    def decorator(func):
        def inner(*args, **kwargs):
            print(f"函数{func.__name__}j将重复执行{n}遍")
            for _ in range(n):
                func(*args, **kwargs)
        return inner
    return decorator

@repeat(2)
def hello(name):
    print(f"Hello, {name}")

hello("tom")
# 函数helloj将重复执行2遍
# Hello, tom
# Hello, tom
(2)有参装饰器模板
def decorators(flag):
    def check_decorator(func):
        def inner(*args, **kwargs):
            return func(*args, **kwargs)
        return inner
    return check_decorator

@decorators(flag)
def func1():
    ...
(3)练习(登录才可以使用的计算器)
  • 要求:
    • 使用一个装饰器decorators
    • 首先需要和字典user_dict进行匹配登录
    • 检验输入的数字是否为数字
    • 除法时除数不能为0
user_dict = {'tom': '000'}

def decorators(flag, tag = None):
    if flag == "login":
        def login_chcek(func):
            def inner(*args, **kwargs):
                username = input("username>>>:").strip()
                password = input("password>>>:").strip()
                if username not in user_dict or password != user_dict.get(username):
                    return  "Username or password is wrong!!"
                return func(*args, **kwargs)
            return inner
        return login_chcek
    if flag == "num":
        def num_check(func):
            def inner(*args, **kwargs):
                num_one = input("first num>>>:").strip()
                num_two = input("second num>>>:").strip()
                if not num_one.isdigit() or not num_two.isdigit():
                    return "Number is not int!!"
                if tag == "div":
                    if num_two == "0":
                        return "The dividend can not be 0!!"
                msg = func(int(num_one), int(num_two), *args, **kwargs)
                return msg
            return inner
        return num_check

@decorators("login")
@decorators("num")
def add(*args, **kwargs):
    return args[0] + args[1]


@decorators("login")
@decorators("num")
def sub(*args, **kwargs):
    return args[0] - args[1]


@decorators("login")
@decorators("num")
def multy(*args, **kwargs):
    return args[0] * args[1]


@decorators("login")
@decorators("num", tag="div")
def div(*args, **kwargs):
    return args[0] / args[1]

print(add())
print(sub())
print(multy())
print(div())

【7】伪装装饰器

(1)help()查看函数文档注释
  • 使用help()函数可以查看函数文档的文档注释,本质就是查看函数的doc的属性
  • 对于被装饰的函数,help()函数用来查看文档注释
def register():
    """
    注册函数
    :return:
    """
    print("注册函数")

print(help(register))

# register()
#     注册函数
#     :return:
# 
# None
        
def register_check(func):
    def inner(*args, **kwargs):
        """
        如果是特定的用户注册,那么给他管理员身份
        :param args:
        :param kwargs:
        :return:
        """
        name = input("name>>>:").strip()
        psw = input("password>>>:").strip()
        if name == "tom" and psw == "000":
            role = "admin"
        else:
            role = "normal"
        return func(*args, **kwargs)

    return inner

@register_check
def register():
    """
    注册函数
    :return:
    """
    print("注册函数")

print(help(register))

# inner(*args, **kwargs)
# #     如果是特定的用户注册,那么给他管理员身份
# #     :param args:
# #     :param kwargs:
# #     :return:
# # 
# # None
        

(2)伪装装饰器(functools

  • 装饰器内的注释有可能包含重要信息,这个信息不想被别人查看
  • help查看的内容将是函数原本的注释内容
from functools import wraps
def register_check(func):
    @wraps(func)
    def inner(*args, **kwargs):
        """
        如果是特定的用户注册,那么给他管理员身份
        :param args:
        :param kwargs:
        :return:
        """
        name = input("name>>>:").strip()
        psw = input("password>>>:").strip()
        if name == "tom" and psw == "000":
            role = "admin"
        else:
            role = "normal"
        return func(*args, **kwargs)

    return inner

# @register_check
def register():
    """
    注册函数
    :return:
    """
    print("注册函数")

print(help(register))

# register()
#     注册函数
#     :return:
#
# None

你可能感兴趣的:(python,开发语言)