python —— 装饰器

本文详细介绍了Python装饰器的原理与使用,包括简单装饰器、带参数的装饰器、类装饰器以及functools模块中的wraps装饰器。通过实例展示了如何使用@classmethod和@staticmethod以及@property装饰器,并探讨了装饰器在函数元信息保留和类方法调用中的作用。同时,文章还讨论了实例、类和私有属性的区别与分类。

1、装饰器简介

  • 1、函数也是一个对象,而且函数对象也可以被赋值给变量,所以通过变量也能调用该函数
  • 2、函数对象有一个__name__属性,可以拿到函数的名字
  • 3、装饰器的作用就是为已经存在的对象添加额外的功能
  • 4、装饰器是在定义函数和类的时候使用

2、简单装饰器

实现我们在跑程序的时候告诉我们运行进度

def decorator_process(func):
    def wrapper(*args,**kargs):
        print("%s已经开始运行"%func.__name__)
        return func(*args,**kargs)
    return wrapper

@decorator_process
def wj_sum(a,b):
    print(a+b)-7
wj_sum(5,6)

3、带参数的装饰器

装饰器允许我们在调用时提供给它参数

def decorator_user(user):
    def decorator(func):
        def wrapper(*args,**kargs):
            print("%s在使用%s函数"%(user,func.__name__))
            return func(*args,**kargs)
        return wrapper
    return decorator

@decorator_user("wj")
def wj_average(a,b):
    print((a+b)/2)
    
wj_average(2,5)

3、类装饰器

  • 相比于函数装饰器,装饰器具有灵活度大、高内聚、封装性等优点,
  • 使用类装饰器还可以依靠内部的__recall__方法,
  • 当使用@形式将装饰器附加到函数上时,就会调用此方法
class decorate_process_cls(object):
    def __init__(self,func):
        self.func = func
    def __call__(self,*args,**kargs):
        print("%s函数开始运行"%self.func.__name__)
        self.func(*args,**kargs)
        print("%s函数运行结束"%self.func.__name__)

@decorate_process_cls
def wj_mean(a,b):
    print((a+b)/2)

wj_mean(2,5)

4、functools.wraps

  • 使用装饰器极大地复用了代码,但是它有一个缺点就是原函数的元信息不见了;
  • 这个问题就比较严重,好在我们有functools.wraps,wraps本身也是一个装饰器;
  • 它能把原函数的元信息拷贝到装饰器函数中,这使得装饰函数也也有和原函数一样的元信息。
from functools import wraps
import datetime

class decorator_process_cls2(object):
    def __init__(self,func):
        self.func = func
    def __call__(self,*args,**kargs):
        """
        这是一个装饰器
        """
        print(datetime.datetime.now(),"%s程序开始运行"%self.func.__name__)
        self.func(*args,**kargs)
        print(datetime.datetime.now(),"%s程序结束运行"%self.func.__name__)

@decorator_process_cls2
def wj_multiply(a,b):
    """
    这是原函数
    """
    print(a*b)

print(dir(wj_multiply))

class decorator_process_cls2(object):
    def __init__(self,func):
        self.func = func
    @wraps
    def __call__(self,*args,**kargs):
        """
        这是一个装饰器
        """
        print(datetime.datetime.now(),"%s程序开始运行"%self.func.__name__)
        self.func(*args,**kargs)
        print(datetime.datetime.now(),"%s程序结束运行"%self.func.__name__)

@decorator_process_cls2
def wj_multiply(a,b):
    """
    这是原函数
    """
    print(a*b)

print(dir(wj_multiply))

5、内置装饰器

  • @classmethod
  • @staticmethod
  • @property

5.1 @classmethod

  • 我们要写一个实例不能调用,而能用类调用的方法,则可添加@classmethod的装饰器
  • 此时函数不需要传self这个参数,但是需要传类这个对象作为参数
class wj_1(object):
    name = "张三"
    age = 18
    @classmethod
    def infor(class_object):
        print(class_object.name,"的年龄是",class_object.age,"岁",sep = "")
        
wj_1.infor()

5.2 @staticmethod

  • 经常有一些跟类有关系的功能,但在运行时又不需要实例和类参与的情况下,
  • 需要用到静态方法,比如更改环境变量或者修改其它类的属性等能用到静态方法,
  • 实例或者类都能调用该方法
class wj_2(object):
    def __init__(self):
        self.name_ = "李四"
        self.age_ = 25
    name = "张三"
    age = 18
    @staticmethod
    def update_name():
        wj_2.name = "王二"
print(wj_2.name)
wj_2.update_name()
print(wj_2.name)
  • 1、@staticmethod 不需要表示自身对象的self参数,也不需要表示自身类的参数class_object,就和使用函数一样
  • 2、@classmethod不需要self,但是需要class_object
class wj_ensemble(object):
    sign = "a处"
    def __init__(self):
        self.sign = "b处"
    @classmethod
    def func_class(class_obj):
        print(class_obj.sign)
    @staticmethod
    def func_static():
        print(wj_ensemble.sign)
    def func_usual(self):
        print(self.sign)
# 实例运行普通类函数
wj_ensemble().func_usual()
# 类运行普通类函数
try:
    wj_ensemble.func_usual
except Exception as error:
    print("类不能调用普通函数")
# 类调用@classmethod装饰的方法
wj_ensemble.func_class()

# 实例不能调用@classmethod装饰的方法
wj_ensemble().func_class()

# 静态函数都可以调用
wj_ensemble.func_static()
wj_ensemble().func_static()

5.3 @property

使用此装饰器以后,实例可以把方法当属性来调用,也就是无法对属性赋值使用,@property最主要的目的是使得对私有属性无法修改

class wj_3(object):
    def __init__(self):
        self.__name__ = "玫瑰少年"
    @property
    def name(self):
        return self.__name__
wj_3().name

6、属性的分类

6.1 实例的属性

  • 1、在def __ init __(self)中初始化
  • 2、内部调用使用self.property
  • 3、外部调用使用instance.property

6.2 类属性

  • 1、在def __ init __(self)外初始化
  • 2、内部调用:classname.property
  • 3、外部调用:classname.property 或者 instance.property

6.3 类属性

  • 1、单下划线开头,只是告诉别人这是私有属性,外部依旧可以更改
  • 2、双下划线开头,不可用实例来更改
<think>好的,我现在需要帮助用户了解Python中的装饰器的使用和实现。用户已经提供了一些相关的引用资料,我需要结合这些资料来整理出一个清晰的回答。首先,我需要明确装饰器的基本概念,然后讨论它们的实现方式,包括不带参数和带参数的情况,同时可能需要对比函数装饰器装饰器的不同。 根据引用[3],装饰器可以通过实现__call__方法来作为装饰器,这似于仿函数或函数对象。引用[1]中的例子展示了一个装饰器如何装饰一个,添加新的方法或修改原有行为。而引用[2]则展示了一个作为装饰器来装饰函数的情况,其中通过__init__和__call__方法来处理被装饰的函数。 首先,我需要解释装饰器的两种主要用途:装饰函数和装饰。对于装饰函数的情况,装饰器通常需要维护被装饰函数的状态,通过__init__方法接收函数,并在__call__中执行装饰逻辑。对于装饰的情况,装饰器可能会返回一个新的,包裹原始,并添加或修改方法,如引用[1]中的例子。 接下来,我需要分别讨论不带参数和带参数的装饰器。不带参数的装饰器相对简单,如引用[2]中的例子,直接在__call__中处理函数执行前后的操作。而带参数的装饰器则需要一个外层函数来接收参数,并返回一个装饰器,这可能涉及到嵌套的结构,如引用[3]中提到的带参数的装饰器实现。 在组织回答时,应该分点说明,先介绍装饰器装饰函数的情况,再讨论装饰的情况。每个部分都需要代码示例来辅助说明,确保用户能够理解不同的应用场景。同时,需要指出装饰器相对于函数装饰器的优势,比如更易于维护状态或支持更复杂的装饰逻辑。 最后,根据用户的要求生成相关问题,这些问题应该围绕装饰器的实现细节、应用场景以及与函数装饰器的对比等方面展开。需要确保问题具有针对性,能够帮助用户进一步深入学习。 现在需要检查是否有遗漏的点,例如是否涵盖了带参数和不带参数的两种情况,是否区分了装饰函数和装饰的不同实现,以及是否引用了正确的资料来支持回答。同时,要确保所有代码示例符合LaTeX格式的要求,行内公式使用$...$,独立公式使用$$...$$,并且正确添加引用标识。</think>### Python装饰器的使用与实现 装饰器Python中可用于装饰**函数**或****,通过实例的`__call__`方法实现装饰逻辑[^3]。其核心在于将装饰过程封装为的调用行为,可分为**不带参数**和**带参数**两种形式。 --- #### 一、装饰器装饰函数 1. **基础形式(无参数)** 通过`__init__`接收被装饰函数,`__call__`实现装饰逻辑: ```python class TimerDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): start = time.time() result = self.func(*args, **kwargs) end = time.time() print(f"执行耗时: {end - start}秒") return result @TimerDecorator def my_function(): time.sleep(1) ``` 调用`my_function()`时会输出执行时间[^2]。 2. **带参数的装饰器** 需外层函数接收参数,返回装饰器: ```python class RetryDecorator: def __init__(self, max_retries): self.max_retries = max_retries def __call__(self, func): def wrapper(*args, **kwargs): for _ in range(self.max_retries): try: return func(*args, **kwargs) except Exception as e: print(f"重试中... 错误: {e}") raise ValueError("超出最大重试次数") return wrapper @RetryDecorator(max_retries=3) def fetch_data(): # 模拟可能失败的请求 if random.random() < 0.7: raise ConnectionError("连接失败") return "数据获取成功" ``` 此装饰器会在函数失败时自动重试指定次数。 --- #### 二、装饰器装饰 1. **添加新方法/属性** 通过返回包装扩展原始的功能: ```python def add_logging(cls): class WrappedClass(cls): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) print(f"初始化 {cls.__name__} 实例") def debug_info(self): return f"对象型: {type(self).__name__}" return WrappedClass @add_logging class DataProcessor: def process(self): print("处理数据") obj = DataProcessor() obj.process() # 输出: 处理数据 obj.debug_info() # 输出: 对象型: WrappedClass ``` 装饰器为原始添加了日志和调试方法[^1]。 2. **修改现有方法** 可在装饰器中覆盖原的方法: ```python class ValidateInputsDecorator: def __init__(self, cls): self.cls = cls def __call__(self, *args, **kwargs): # 对输入参数进行校验 if not all(isinstance(arg, int) for arg in args): raise TypeError("参数必须为整数") return self.cls(*args, **kwargs) @ValidateInputsDecorator class Calculator: def __init__(self, a, b): self.a = a self.b = b def add(self): return self.a + self.b calc = Calculator(2, 3) # 正常创建实例 calc = Calculator("2", 3) # 触发TypeError ``` --- #### 三、装饰器 vs 函数装饰器 | 特性 | 装饰器 | 函数装饰器 | |---------------------|-----------------------------------|--------------------------| | **状态管理** | 通过实例属性保存状态更直观 | 需闭包或`nonlocal`关键字 | | **代码结构** | 适合复杂装饰逻辑(如多步骤处理) | 适合简单逻辑 | | **可扩展性** | 可通过继承扩展功能 | 灵活性较低 | ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值