9-2为被装饰的函数保存元数据

本文深入探讨了Python函数的多个重要特性,包括默认参数、闭包、装饰器等,并通过实例展示了如何正确使用这些特性来避免常见陷阱。
# -*- coding:utf-8 -*-

'''f.__name__
   f.__doc__
   f.__module__    所属模块
   f.__defaults__  默认参数
   f.__closure__   闭包'''

import functools

def f(a, b = 1, c = []):
    '''func for print abc'''
    print a, b, c
    return

def f_():
    a = 2
    b = 10
    c = 100
    return lambda k: a ** k + b

def mydecorator(func):
    @functools.wraps(func)           # 新添加的一行
    def wrapper(*args, **kargs):
        '''wrapper function'''
        print 'IN wrapper'
        func(*args, **kargs)
    return wrapper

@mydecorator
def example():
    '''example function'''
    print 'IN example'
# 如果现在调用 print example.__name__    print example.__doc__将会返回wrapper的信息
# 使用标准库 functools 中的装饰器 wraps 装饰内部包裹函数

if __name__ == '__main__':
    print f.__name__
    print f.__doc__
    print f.__module__
    print f.__defaults__
    print f.__closure__

    f.__defaults__[1].append('abc')
    f(100)
    print f.__defaults__ #所以在默认参数里面不要使用可变对象

    p = f
    print p.__name__

    print '*' * 30
    print f_.__closure__
    g = f_()
    print g.__closure__
    c = g.__closure__[2]
    # 现在运行程序会产生‘IndexError: tuple index out of range’错误
    # 只有匿名函数中使用到的 a, b 在闭包里
    print c.cell_contents

    print '*' * 30
    print g(10)         # 原来 ** 运算符是次数
<think>嗯,用户想了解Python装饰器中内部函数的调用方式和使用示例。首先,我需要回忆一下装饰器的基础知识。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。通常装饰器内部会定义一个包装函数,也就是内部函数,这个内部函数会包裹原函数,添加一些额外的功能。 用户的问题是关于内部函数如何调用和使用示例的。我需要先解释装饰器的基本结构,然后展示内部函数是如何被调用的。可能还需要提到闭包的概念,因为内部函数通常会捕获外部函数的变量,比如原函数。 接下来,用户可能需要具体的例子来理解。我应该提供一个简单的装饰器示例,比如计算执行时间的装饰器,或者日志记录的装饰器。在例子中,内部函数(比如wrapper)会接收*args和**kwargs,然后调用原函数,并添加额外的代码。 另外,用户提到的问题可能需要涉及类装饰器,根据用户提供的引用内容,引用了类装饰器的例子。所以可能需要区分函数装饰器和类装饰器的不同。比如,类装饰器中的__call__方法,或者像引用[1]中的类方法作为装饰器的情况。 需要注意的是,装饰器在应用时,内部函数通常会被返回,当装饰后的函数被调用时,实际上调用的是内部函数。例如,在@decorator的语法下,原函数被替换为装饰器返回的函数,也就是内部函数。 可能还需要解释为什么需要在内部函数中使用functools.wraps,这是为了保留原函数元数据,比如__name__等属性。这虽然是一个细节,但对于正确使用装饰器很重要。 用户提供的引用中有一个例子是类装饰器,其中info方法作为装饰函数,返回原函数。这种情况下,装饰器类实例的方法作为装饰器,可能也需要说明这种情况下的调用流程。 另外,引用[2]提到了类装饰器中的__call__方法,当装饰后的函数被调用时,__call__方法会自动执行。这可能涉及到类作为装饰器的情况,这时候类的实例需要是可调用的,即实现__call__方法。 综合这些信息,我需要组织一个结构清晰的回答,首先介绍装饰器的基本概念,然后展示函数装饰器和类装饰器的例子,说明内部函数的调用方式,并引用用户提供的参考资料中的例子来说明不同的装饰器实现方式。 同时,根据用户的要求,生成相关问题,可能涉及装饰器的应用场景、不同装饰器类型的区别、保留元数据的方法,以及类装饰器的其他用法等。</think>### Python装饰器中内部函数的调用方式解析 装饰器在Python中通过**“函数嵌套+闭包”**的机制实现功能扩展。其核心逻辑是:**内部函数接收被装饰函数的参数,并在调用原函数前后添加额外逻辑**。以下通过具体示例说明调用过程: --- ### 一、函数装饰器示例(统计执行时间) ```python import time from functools import wraps def timer_decorator(func): @wraps(func) # 保留原函数元信息 def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) # 调用原函数 end = time.time() print(f"{func.__name__}执行耗时: {end - start:.2f}秒") return result return wrapper # 返回内部函数 @timer_decorator def calculate_sum(n): return sum(range(n+1)) calculate_sum(1000000) ``` **调用流程说明:** 1. 当`calculate_sum`被调用时,实际执行的是`wrapper`函数 2. `wrapper`通过`func(*args, **kwargs)`调用原函数 3. 执行前后分别记录时间差[^2] --- ### 二、类装饰器示例(记录日志) ```python class LoggerDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print(f"开始执行: {self.func.__name__}") result = self.func(*args, **kwargs) # 调用原函数 print(f"执行完成: {self.func.__name__}") return result @LoggerDecorator def multiply(a, b): return a * b multiply(3, 4) ``` **类装饰器特点:** - 通过实现`__call__`方法使实例可调用 -函数保存在实例属性中 - 调用时自动触发`__call__`方法 --- ### 三、带参数的装饰器(权限验证) ```python def role_required(role): def decorator(func): def wrapper(*args, **kwargs): if check_user_role(role): # 假设存在权限验证函数 return func(*args, **kwargs) else: raise PermissionError("权限不足") return wrapper return decorator @role_required("admin") def delete_file(filename): # 删除文件操作 pass ``` **参数传递路径:** 1. `role_required("admin")`返回`decorator`函数 2. `@decorator`应用装饰器,返回最终的`wrapper`函数 3. 多层嵌套实现参数传递[^1] --- ### 四、保留原函数属性的重要性 使用`@wraps(func)`装饰内部函数- 保持`__name__`、`__doc__`等元数据 - 避免调试时出现函数名混淆 - 对依赖函数签名的库(如Flask路由)至关重要 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值