首页 > 编程语言 >什么是Python中的闭包与装饰器

什么是Python中的闭包与装饰器

时间:2024-07-24 18:59:35浏览次数:18  
标签:闭包 function 函数 Python print 装饰 def

1.闭包

闭包(Closure)是指在一个函数内部定义的函数,并且这个内部函数可以访问其外部函数作用域中定义的变量。在 Python 中,闭包是一个强大且灵活的编程工具,可以实现许多有趣和实用的功能。

让我们通过一个简单的示例来说明闭包的基本概念:

def outer_function(x):
    def inner_function(y):  # 内部函数 
        return x + y 
    return inner_function # 外部函数返回内部函数的引用 
closure = outer_function(10) # 创建闭包 
result = closure(5) # 调用闭包 
print(result) # 输出 15

在这个例子中:

  • outer_function 是外部函数,接受一个参数 x
  • inner_function 是内部函数,它接受参数 y,并返回 x + y 的结果。
  • outer_function(10) 调用返回了 inner_function 的引用,并且在这个过程中 x 被设置为 10。
  • 我们将 outer_function(10) 的返回结果赋给 closure,这个 closure 就成为了一个闭包。
  • 当我们调用 closure(5) 时,实际上是在调用 inner_function,这时闭包中的 x 值为 10,所以结果为 10 + 5 = 15。

总结三点就是:闭包的形成一定是有外部函数嵌套内部函数、外部函数返回值为内部函数、内部函数可以访问外部函数的局部变量。

闭包在实际编程中有许多应用,例如:

  • 工厂函数:通过闭包可以动态生成函数,根据不同的输入参数生成不同的逻辑。
  • 装饰器:装饰器本质上就是闭包,它可以在不修改函数源代码的情况下,添加额外的功能。
  • 回调函数:将函数作为参数传递给其他函数,形成闭包,可以在异步编程中非常有用。

在使用闭包时,需要注意以下几点:

  • 变量绑定时间:闭包中的自由变量在定义时被绑定,而不是在调用时。
  • 内存管理:闭包会保留外部函数的环境,可能会导致内存占用问题,尤其是在闭包的作用域很大或者存在循环引用时。

2.装饰器

Python装饰器是一种高级功能,它允许在不改变函数本身代码的情况下,动态地修改或者增强函数的行为。装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数作为输出。在函数执行之前或之后,装饰器允许添加额外的功能。

让我们通过一个简单的示例来说明装饰器的基本用法:

def decorator_function(func): # 定义一个装饰器函数 
    
    def wrapper(): 
        print("Wrapper executed this before {}".format(func.__name__)) 
        return func() # 执行原始函数 
    
    return wrapper


def say_hello():  # 定义一个普通函数 

    print("Hello!")  


say_hello = decorator_function(say_hello) # 使用装饰器来增强函数的功能
say_hello()# 调用经过装饰的函数 

在这个例子中:

  • decorator_function 是一个装饰器函数,它接受一个函数 func 作为参数。
  • wrapper 函数是装饰器内部定义的函数,它在调用被装饰的函数之前添加了额外的功能。
  • 最后,我们通过 say_hello = decorator_function(say_hello) 将 say_hello 函数应用了 decorator_function 装饰器。

可以看出,其实装饰器是合理运用了闭包的特性,在内部函数调用了外部函数的形参函数。在内部函数对调用的函数进行功能装饰,实现不改变原有函数功能的基础上增添了新的功能实现。并且,不单单是某个函数可以被内部函数调用进行装饰,还可以有很多个一起。

在上述例子中,还可以换一种专属于装饰器的写法。

def decorator_function(func): # 定义一个装饰器函数 
    
    def wrapper(): 
        print("Wrapper executed this before {}".format(func.__name__)) 
        return func() # 执行原始函数 
    
    return wrapper


@decorator_function  # 在函数前加一顶“帽子”,用@ 将 target_function 函数传递给 decorator 装饰器,并将返回的函数重新赋值给 target_function。
def say_hello():  # 定义一个普通函数 

    print("Hello!")  


say_hello()  # 调用经过装饰的函数 

可以看出,装饰器通过 @ 符号应用在函数定义之前,会将 target_function 函数传递给 decorator 装饰器,并将返回的函数重新赋值给 target_function。从而,每次调用 target_function 时,实际上是调用了经过装饰器处理后的函数。

装饰器的应用场景

  • 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
  • 性能分析: 可以使用装饰器来测量函数的执行时间。
  • 权限控制: 装饰器可用于限制对某些函数的访问权限。
  • 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

通过装饰器,开发者可以在保持代码整洁的同时,灵活且高效地扩展程序的功能。

3.装饰器案例

 在几个案例中去了解一下装饰器的一些使用情况和方式。

user = None


def index():
    print("欢迎来到小宇家杂货铺")


def login_required(f):
    def check():
        global user
        if user:
            f()
        else:
            while True:
                username = input("请输入用户名")
                password = input("请输入密码")
                if username == "cxy" and password == "123456":
                    f()
                    user = {"username": username, "password": password}
                    break
                else:
                    print("用户名和密码错误")
    return check


@login_required
def go_center():
    print("进入个人中心成功")


@login_required
def go_cart():
    print("进入购物车成功")


@login_required
def go_search():
    print("进入搜索页成功")


index()
go_center()
go_cart()

上述案例是模仿在某购物平台进行的用户登录,无论在哪里进行用户登录,之后都可以通过登录数据跳过登录选项,也是运用装饰器的功能实现装饰三个需要相同功能的地方。

不仅如此,装饰器函数也可以接受参数。

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

以上代码中 repeat 函数是一个带参数的装饰器,它接受一个整数参数 n,然后返回一个装饰器函数。该装饰器函数内部定义了 wrapper 函数,在调用原始函数之前重复执行 n 次。因此,greet 函数在被 @repeat(3) 装饰后,会打印三次问候语。

Python 还允许将多个装饰器堆叠在一起,按照从上到下的顺序应用:

def decorator1(func): 
    
    def wrapper(): 
        print("Decorator 1 executed") 
        return func() 
    return wrapper 


def decorator2(func): 

    def wrapper(): 
        print("Decorator 2 executed") 
        return func() 
    return wrapper 


@decorator1 
@decorator2 
def say_hello(): 
print("Hello!") 


say_hello()

在这个例子中,先应用 @decorator2,再应用 @decorator1,最终调用 say_hello() 时会按照 Decorator 1 executedDecorator 2 executed 的顺序输出。

最后运用装饰器来实现对比两个函数的执行效率。

import random
import time

datas = [random.randint(0, 10000) for i in range(10000)]
# 浅拷贝一个数据,得到两个一样的值
data = datas.copy()


# 定义一个时间耗费函数装饰器
def time_coast(f):

    def calc():
        start = time.time()
        f()
        print(time.time() - start)
    return calc


# 对函数一进行装饰
@time_coast
def fun1():
    # 对数据进行方法一排序
    datas.sort()
    print(datas)


# 对函数二进行装饰
@time_coast
def fun2():
    # 对浅拷贝出来的数据进行方式二排序
    new_data = sorted(data)
    print(new_data)


# 执行装饰后的函数,比较两个函数花费时间那个少
fun1()
fun2()

 可以看出,装饰器的调用,实现了查看两函数的执行耗费时间,但是没有改变原有函数的功能。装饰器的存在,让我们在编程时,可以给函数添加更多更丰富的功能的同时,又不会让我们的代码看起来繁琐杂乱。

 

标签:闭包,function,函数,Python,print,装饰,def
From: https://blog.csdn.net/2402_86120726/article/details/140617636

相关文章

  • Python-无ABI文件打包EVM合约方法名及参数方法
    #pipinstalleth-abiimporteth_abi#pipinstallsafe-pysha3fromsha3importkeccak_256defkeccak_256_hash(data:str)->bytes: k=keccak_256() k.update(data.encode()) returnk.digest()defpack_abi_data(method:str=None,params:list=No......
  • pycharm配置及python环境相关配置
     python虚拟环境不同项目依赖的第三方包的版本可能不一样,这样一个环境就没法同时开发不同的项目,所以需要创建不同的虚拟环境virtualenv用户创建独立的python环境,多个python项目互相独立互不影响安装方法pipinstallvirtualenv创建虚拟环境virtualenvvenv会......
  • [SUCTF 2019]Pythonginx(url中的unicode漏洞引发的域名安全问题)
    @app.route('/getUrl',methods=['GET','POST'])defgetUrl():#从请求中获取url参数url=request.args.get("url")host=parse.urlparse(url).hostname#第一处检查主机名是否为'suctf.cc'ifhost=='s......
  • Python实现RSA加密算法,让你的信息更加安全
    一、什么是编码    想要实现加密就必须要先了解什么是编码。    编码是信息从另一种形式或格式转换为另一种形式或格式的过程,解码则是编码的逆过程。字符编码(CharacterEncoding)是把字符集中的字符编码为指定集合中的某个对象,以便信息在计算机中传输。在密码......
  • Python 中的工作队列 - 我错过了什么吗?
    这可能会被标记为重复或可能不相关。但我实际上相信这个问题对我和未来缺乏经验的Python开发人员都很重要。由于GIL,用于CPU密集型任务的本地工作队列的概念在Python中至关重要。这方面SE上有明显的答案。使用子进程的方法来绕过缺乏真正的CPU有限并行性的问题。在Pyth......
  • Python ctypes OSError:[WinError 1114]动态链接库(DLL)初始化例程失败
    我试图使用Python中的ctypes库调用C++函数:test.pyfromctypesimport*fromrandomimportrandinttester=cdll.LoadLibrary('./test.dll')print(tester.test(randint(1,100)))test.cpp#include<vector>intcppTest(intnum){std:......
  • Python 的分布式锁管理器
    我有一堆具有多个实例的服务器,这些实例访问的资源对每秒的请求有硬性限制。我需要一种机制来锁定所有正在运行的服务器和实例对此资源的访问。有我在github上找到的一个restful分布式锁管理器:https://github.com/thefab/restful-distributed-lock-manager不幸......
  • 如何在Python中提示self变量的类型
    通常不需要关心Python类成员函数中的self变量,但我正在实现一个装饰器,看起来像defextractor(depends:List[Text]=None,provides:List[Text]=None)->Callable[[ExtractorFunction],Extracto......
  • 将 Python 类型提示限制为元组中的值
    我的方法之一采用status中使用的filter()参数。此参数与定义如下的模型字段相关:STATUS_CHOICES=((1,_("draft")),(2,_("private")),(3,_("published")),)classMyModel(Model):status=models.PositiveSmallIntegerFi......
  • 在python中查找区间数据的中位数
    我正在探索不同的python库,我想知道如何找到分组数据集的近似中值。这里有一个表格供参考。年龄频率1-1012310-203502......