深入理解Python中的闭包和装饰器

        在Python编程中,闭包和装饰器是两个极其强大的特性,它们提供了代码复用和增强的高级手段。本文将深入探讨这两个概念,通过实例展示它们的应用和特点。

一、闭包:记忆的力量

        闭包(Closure)是指在一个内部函数中,对外部作用域的变量进行引用。简单来说,闭包使得函数可以记住并访问其外部函数的变量,即使外部函数已经执行完毕。

闭包的特点:

  • 外部函数嵌套内部函数
  • 外部函数将内部函数返回
  • 内部函数可以访问外部函数的局部变量
a = 20

def my_fun1():
    print(f'my_fun1')
    i = 10

    def my_fun2():
        nonlocal i  # 声明 i 是局部变量
        global a    # 声明 a 是全局变量
        a += 30
        i += 20
        print(f'my_fun2', a, i)

    my_fun2()
    return my_fun2


r = my_fun1

r()   #第一次执行
# my_fun1
# my_fun2 50 30 


r()    #第二次执行
# my_fun1
# my_fun2 80 30

r()    #第三次执行   
# my_fun1
# my_fun2 110 30

        请注意,每次直接调用 r() 实际上是调用 my_fun2,而 i 的值会在每次 my_fun1 调用时重新初始化,因为 imy_fun1 的局部变量。而 a 是全局变量,所以它的值会在每次调用 my_fun2 时累积增加。

二、装饰器:函数的魔法外衣

        装饰器(Decorator)是一种设计模式,用于在不修改原始函数代码的情况下,增加函数的功能。装饰器本身是一个函数,它接受一个函数作为参数,并返回一个新的函数。

装饰器的特点:

  • 增强函数功能:在不改变原有逻辑的情况下,增加新的功能。
  • 可重用性:装饰器可以应用于任何函数,实现功能的重用。
  • AOP(面向切面编程):用于日志记录、性能测试、事务处理等跨关注点问题。
# 装饰器:一个函数,用于增强或修改另一个函数的行为,通常通过返回一个新的函数来实现。
import random
import time

datas = [random.randint(0, 10000) for i in range(10000)]
# 通过浅拷贝 得到一模一样的列表
datas_copy = datas.copy()


def time_cost(f):
    def calc():
        stat = time.time()
        f()
        print(f'{f}花费的时间开销为:{time.time() - stat}')

    return calc


@time_cost
def my_fun1():
    datas.sort()
    print(datas)


my_fun1()


@time_cost
def my_fun2():
    new_list = sorted(datas_copy)
    print(new_list)


my_fun2()

这里使用 @time_cost 语法,我们为 my_fun 添加了打印随机数花费的时间开销。       

         为了能让大家更好的理解装饰器换一种写法来演示下:

# 装饰器:一个函数,用于增强或修改另一个函数的行为,通常通过返回一个新的函数来实现。
import random
import time

datas = [random.randint(0, 10000) for i in range(10000)]
# 通过浅拷贝 得到一模一样的列表
datas_copy = datas.copy()


def time_cost(f):
    def calc():
        stat = time.time()
        f()
        print(f'{f.__name__}花费的时间开销为:{time.time() - stat}')

    return calc


def fun1():
    datas.sort()
    print(datas)


fun1 = time_cost(fun1)
fun1()  # 此时的fun1()不是fun1()而是calc()


def fun2():
    new_list = sorted(datas_copy)
    print(new_list)


fun2 = time_cost(fun2)
fun2()  # 此时的fun2()不是fun2()而是calc()

三、 闭包与装饰器的结合

        那怎么解决 被修饰的函数有参数的情况呢?我们还拿上边的例子举例:

# 解决 被装饰的函数有参数
import random
import copy
import time

datas = [random.randint(0, 10000) for i in range(10000)]
datas_copy = copy.deepcopy(datas)


def time_cost(f):
    def calc(sort_type):
        start = time.time()
        f(sort_type)
        print(f'函数{f.__name__}消耗的时间为:{time.time() - start}')

    return calc


def my_fun1(sort_type):
    datas.sort(reverse=sort_type)
    print(datas)


r = time_cost(my_fun1)
r(True)


def my_fun2(sort_type):
    new_datas = sorted(datas_copy, reverse=sort_type)
    print(new_datas)


r = time_cost(my_fun2)
r(True)
  • 当调用 r(True) 时,实际上是调用了装饰后的 my_fun1 函数,并传递 True 作为参数。这会测量 my_fun1 函数执行所需的时间,并打印出排序后的 datas 列表。
  • 接着,再次调用 r(True) 时,实际上是调用了装饰后的 my_fun2 函数,并传递 True 作为参数。这会测量 my_fun2 函数执行所需的时间,并打印出新的排序列表 new_datas

  这里再附带一个实际的代码,当我们进入某网站首页时,登陆了才能看购物车和个人中心。

user = None


def login_req(f):
    def check():
        global user
        if user:
            f()
        else:
            while True:
                username = input('请输入用户名:')
                password = input('请输入密码:')
                if username == 'admin' and password == '123456':
                    user = username
                    f()
                    break
                else:
                    print('用户名或密码错误')

    return check


def index():
    print(f'我是首页')


@login_req
def center():
    print(f'我是个人中心')


@login_req
def cart():
    print(f'我是购物车')


index()

center()

cart()

结论

        闭包和装饰器是Python中两个强大的工具,它们提供了代码复用和增强的新途径。闭包以其记忆作用域的能力,允许函数访问外部作用域的变量;而装饰器以其增强功能和可重用性,允许我们在不修改原始函数代码的前提下,增加新的功能。理解并掌握这两个概念,将极大地提升你的Python编程能力。

 

相关推荐

  1. 深入理解Python装饰

    2024-07-22 22:28:04       16 阅读
  2. python -装饰

    2024-07-22 22:28:04       28 阅读
  3. Python笔记11-装饰设计模式

    2024-07-22 22:28:04       49 阅读
  4. Python--装饰高级应用

    2024-07-22 22:28:04       18 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-22 22:28:04       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-22 22:28:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-22 22:28:04       45 阅读
  4. Python语言-面向对象

    2024-07-22 22:28:04       55 阅读

热门阅读

  1. C++ STL nth_element 用法

    2024-07-22 22:28:04       13 阅读
  2. 低空经济“芯”挑战

    2024-07-22 22:28:04       17 阅读
  3. Python应用—给暑假熊孩子出算术题

    2024-07-22 22:28:04       17 阅读
  4. Math Reference Notes: 数学思想和方法

    2024-07-22 22:28:04       14 阅读
  5. Flask: URL 视图函数 路由

    2024-07-22 22:28:04       16 阅读
  6. web前端 React 框架面试200题(四)

    2024-07-22 22:28:04       14 阅读
  7. Redis 持久化详解

    2024-07-22 22:28:04       15 阅读
  8. 设计模式-抽象工厂模式

    2024-07-22 22:28:04       11 阅读