Python 装饰器 (面向切面编程,语法糖,AOP)

intro

        本文主要记录Python decorator相关的知识。最近想要使用插桩去对一些库进行魔改的时候,发现某些库并没有提供显式的插桩接口。但是这些库函数的代码又确实十分复杂,于是就想到了直接在函数层面进行插桩,这样虽然自动化程度变低了,但是控制粒度更加精细。笔者之前曾经学习过关于Spring框架的相关知识,于是就想Python中是否也有类似的AOP机制,了解之后发现,原来就是decorator,纵观这些玩意有各种名字,但是本质思想其实一模一样。

理解Python中函数的本质

        本着万物皆可对象的原则,函数实际上也是一种Object!所以其实函数本身也可以当做一种参被各种操作!比如:

def add(a):
    return a+1

def addplus(func):
    def wrap(a):
        print("Test!")
        res=func(a)
        return res
    return wrap

print(addplus(add)(1))

Decorator函数

        以上代码可以看出,这么去对函数进行加工然后调用不免有点麻烦,所以就可以用更轻松地机制。使用@就可以轻松完成对函数的改造。

def addplus(func):
    def wrapper(*args,**kwargs):
        print("Test!")
        res=func(*args,**kwargs)
        return res
    return wrapper

@addplus
def add(a):
    return a+1


print(add(1))

        在上面这个示例中,@addplus的的效果等同于:

add=addplus(add)

        然后就会有人想到既然函数能作为一个普通的object被传来传去,那能不能来个套娃,像下面这样:

def addpp(str1):
    def addplus(func):
        def wrapper(*args,**kwargs):
            print(str1)
            res=func(*args,**kwargs)
            return res
        return wrapper
    return addplus
    
@addpp("Test")   
def add1(a):
    return a+1

print(add1(1))

其实这种形式就相当于给decorator套了一层decorator,所以等价于:

add1=addpp("Test")(add1)

Decorator 类

根据前面说的,其实装饰器的本质就是定义函数的时候把定义的函数当成一种参数,毕竟它自己本身就是一种对象。然后传递给一个Decorator函数,那么就能够想到类的初始化的时候,是不是也可以接受函数作为它的一个成员呢,并且我们再定义一个__call__不就能达到相同的效果了吗?

class addcls:
    def __init__(self,func):
        self.func=func
    
    def __call__(self,*args,**kwargs):
        print("Test!")
        res=self.func(*args,*kwargs)
        return res
    
@addcls    
def add2(a):
    return a+1

print(add2(1))

其实这就相当于,add2变成了一个addcls的对象!

adds=addcls(add2)

当然也可以这么去实现:这时候add2就相当于是一个class里边定义的wrapper函数

class addcls:
    def __init__(self,str1):
        self.str1=str1
    
    def __call__(self,func):
        def wrapper(*args,**kwargs):
            print(self.str1)
            res=func(*args,**kwargs)
            return res
        return wrapper
        
    
@addcls("Test")    
def add2(a):
    return a+1

print(add2(1))

类的Decorator

既然函数能被当做对象,那么类本身也可以被当做一个对象被作为参数传递给函数。注意这里是类,并不是实例化后的对象!这样说比较抽象,不妨就把类当成一堆代码块就好,我们可以对代码块进行一些操作。所以也会有类的Decorator,以下是一个例子:

def addclsfunc(cls):
    def __str__(self):
        return str(self.__dict__)
    cls.__str__=__str__
    return cls
    
@addclsfunc
class addcls2:
    def __init__(self,a,b):
        self.a=a
        self.b=b
        
Addcls2=addcls2(1,2)
print(Addcls2)

在类中定义Decorator:

这个问题稍微就比较复杂一点,首先让我么来了解两点:

1. @staticmethod(静态方法)

@staticmethod 用于定义不依赖实例或类的函数。它不需要传入 self(实例)或 cls(类)参数,因此只能访问方法内部的局部变量,不能访问类属性或实例属性。使用类或者实例均可调用!

使用场景:当方法只是一个工具函数,不需要访问或修改类或实例的状态时,适合用静态方法。例如,进行一些数学运算或辅助性的操作。

2. @classmethod(类方法)

@classmethod 用于定义绑定在类上的方法。类方法的第一个参数是 cls,代表类本身。类方法可以访问和修改类属性(而不是实例属性)。

使用场景:当需要创建工厂方法或想对类级别的属性进行访问或修改时,使用类方法比较合适。例如,通过类方法提供另一种创建实例的方式,或者用于管理类的状态。

一定要理解在python中类和对象是两个不同的概念,是两种不同的object!类的成员和类实例化对象的成员是独立的!

class test:
    a=1
    def __init__(self,a):
        self.a=a

t=test(2)
print(t.a)
print(test.a)

在类中定义Decorator的形式有如下几种:

最后这种形式无论是对象调用或者类调用都能正常工作!

<think>好的,我现在需要帮助用户了解AOP面向切面编程)的概念及入门知识。首先,我要回忆自己之前学过的AOP相关知识,并结合用户提供的引用内容来组织回答。 用户的问题包括入门、介绍、原理,所以需要从基础开始解释。AOP面向切面编程,用于处理横切关注点,比如日志、事务等,这些在传统OOP中可能分散在各处。我需要先定义AOP,说明它的作用和优势,比如解耦、代码复用等。 然后,用户提供的引用中有提到核心概念,比如切面、连接点、通知、切点、织入等。这些需要逐一解释,确保用户能理解每个术语的含义。例如,引用3提到织入(weave)是将通知应用到目标对象生成代理的过程,而Spring采用运行时织入。这部分需要详细说明,可能还要对比编译期织入和运行时织入的区别。 接下来是AOP的原理,用户可能想知道底层如何实现。根据引用3,Spring AOP使用了动态代理技术,具体是JDK动态代理和CGLIB。需要解释这两种代理的适用场景,比如基于接口还是类。此外,环绕通知的处理,如引用5中的例子,说明如何正确使用环绕通知处理返回值,避免类型不匹配的问题。 用户提供的引用中还提到了Spring AOP的配置方式,比如基于注解和XML。需要简要介绍这两种方式,并给出一个简单的例子,如引用4中的通知类,展示前置和后置通知的配置。同时,结合示例代码,让用户更直观地理解如何实现一个切面。 可能还需要比较AOP与其他编程思想的区别,比如OOP,说明AOP如何补充OOP的不足,处理横切关注点。引用3提到AOP解决OOP中非核心代码冗余和维护问题,这点需要强调。 最后,生成相关问题时,要考虑用户可能进一步想了解的内容,比如AOP的应用场景、实现方式对比、Spring AOP与其他框架(如AspectJ)的区别、通知类型的使用细节,以及如何解决常见错误(如引用5中的返回值问题)。 需要确保回答结构清晰,分点解释,使用用户可见的中文,避免技术术语过于晦涩,同时引用提供的参考资料来支持内容,比如引用3关于织入和动态代理的部分,引用4关于通知配置的例子,引用5关于环绕通知的注意事项。 可能还需要检查是否有遗漏的核心概念,确保覆盖切面、连接点、通知类型、切点表达式、织入方式、动态代理实现等关键点。同时,示例代码要符合用户提供的格式,使用正确的语法,比如Python的示例可能换成Java,但用户给的引用中有Spring的Java代码,所以保持Java示例更合适。 总结来说,回答的结构应该是:概念定义、核心概念解释、原理(动态代理)、实现方式(注解示例)、优缺点和应用场景,最后生成相关问题。需要确保每个部分都有引用支持,并且语言通俗易懂,帮助用户逐步理解AOP的入门知识。</think>### AOP面向切面编程)概念及入门知识 #### 1. AOP 核心概念 **AOP面向切面编程)** 是一种编程范式,用于将**横切关注点**(如日志、事务、权限校验等)从业务逻辑中分离,实现代码的模块化和复用。其核心思想是通过“切面”将分散在多个类中的公共行为集中管理,避免代码冗余和耦合[^3]。 **核心概念**: - **切面(Aspect)**:封装横切关注点的模块,包含通知(Advice)和切点(Pointcut)。例如,日志记录模块可以视为一个切面。 - **通知(Advice)**:切面在特定连接点执行的动作,分为: - **前置通知(Before)**:在方法执行前触发。 - **后置通知(AfterReturning)**:在方法正常返回后触发。 - **环绕通知(Around)**:包裹目标方法,控制其执行流程[^4][^5]。 - **连接点(Join Point)**:程序执行过程中可插入切面的点,如方法调用或异常抛出。 - **切点(Pointcut)**:通过表达式定义哪些连接点需要被切面处理。例如:`@Pointcut("execution(* com.service.*.*(..))")`。 - **织入(Weave)**:将切面应用到目标对象生成代理对象的过程。Spring AOP 默认使用**运行时动态代理**实现[^3]。 #### 2. AOP 实现原理 Spring AOP 基于**动态代理**技术,具体分为两种方式: 1. **JDK 动态代理**:适用于目标类实现了接口。 2. **CGLIB 代理**:适用于未实现接口的类,通过生成子类代理。 **示例**:环绕通知需正确处理返回值(避免类型不匹配)[^5]: ```java @Around("pt2()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before advice ..."); Object ret = pjp.proceed(); // 调用原始方法 System.out.println("around after advice ..."); return ret; // 返回原始方法的返回值 } ``` #### 3. Spring AOP 快速入门 **基于注解的实现**: 1. 定义切面类,标记 `@Aspect`。 2. 使用 `@Before`、`@Around` 等注解声明通知类型。 3. 通过切点表达式指定生效范围。 **示例**: ```java @Aspect @Component public class LogAspect { @Before("execution(* com.service.*.*(..))") public void beforeAdvice() { System.out.println("方法执行前记录日志"); } } ``` #### 4. AOP 的优缺点 - **优点**: - 解耦核心业务与非核心逻辑(如日志)。 - 提升代码复用性和可维护性。 - **缺点**: - 过度使用可能导致调试困难。 - 动态代理对性能有轻微影响。 #### 5. 应用场景 - 日志记录 - 事务管理 - 权限校验 - 性能监控
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值