python学习笔记--装饰器

目录

1.函数装饰器

1.1语法

1.2实现

1.3 保留原函数信息

2.类装饰器

2.1语法及实现

2.2重定义运算符重载

 3.装饰器嵌套

 4.装饰器参数


1.函数装饰器

函数装饰器允许修改或增强函数或方法的行为,而不需要改变其实际的代码。它接收一个函数作为参数,并返回一个新的函数(可以是原函数本身或新的可调用对象)。可以用来为原始函数添加一些额外的功能。

1.1语法

def decorator(F):  #接受一个被装饰的函数参数,使用@decorator时调用
    #添加额外操作
    return F       #可以返回一个不同的可调用对象,与F有相同数目的参数

@decorator       # func = decorator(func)
def func():      
    ...
  
func()           # 相当于decorator(func)(),其中decorator(func)在@decorator语句已运行了

1.2实现

1)使用嵌套函数实现

def decorator(func):
    def wrapper(*args,**kwargs):
        print("before func")
        func(*args,**kwargs)
        print("after func")
    return wrapper    #返回了一个不同的可调用对象

@decorator
def say_hello(name):
    print("Hello " + name)

say_hello("everyone")

#输出结果:
before func
Hello everyone
after func

2)使用类实现

class decorator:
    def __init__(self,func):       #运行@decorator时,绑定函数到装饰器类的实例
        self.func = func
    def __call__(self,*args):      #调用被装饰函数时,实际为装饰器实例的调用
        #可添加额外操作
        self.func(*args)

@decorator                 # say_hello = decorator(say_hello),创建了一个装饰器实例
def say_hello(name):
    print("Hello " + name)

say_hello("everyone")

    注意方法装饰

    用嵌套函数实现的装饰器可用于装饰方法,但用类实现的装饰器不能应用于方法。

class Decorator:
    def __init__(self,func):
        self.func = func
    def __call__(self,*args):    
        self.func(*args)          #调用失败,C实例不在args中

class C:    
    @Decorator
    def say_hello(self,name):
        print("Hello " + name)

C().say_hello("name")

当装饰器的方法__call__运行后,self接收Decorator类的实例,而args中只一个“name”参数,不包含类C的实例, 在调用方法say_hello时时缺少参数,调用失败。

1.3 保留原函数信息

@functools.wraps 可以复制原始函数的元信息到装饰器返回的新函数中,保留原函数的属性 。

from functools import wraps

def decorator(f):
    @wraps(f)
    def wrapper(*args,**kwargs):
        print("calling decorated function")
        return f(*args,**kwargs)
    return wrapper

@decorator
def example():
    '''
    DocString example
    '''
    print("called function example")

example()
print(example.__name__)
print(example.__doc__)

#输出:
calling decorated function
called function example
example

    DocString example

2.类装饰器

类装饰和函数装饰器使用相同的语法和相似的编程方式,但它是管理类的一种方式,使用额外逻辑来完成实例构造调用的一种方式。 

2.1语法及实现

@decorator
class C:        #等效于C = decorator(C) 
    pass

示例:装饰器创建并返回一个类

def decorator(cls):
    class Wrapper:
        def __init__(self,*args):
            self.wrapper = cls(*args)         #调用被装饰类创建实例
        def __getattr__(self, item):
            return getattr(self.wrapper, item)  #获取属性时从被装饰的类的实例获取
    return Wrapper

@decorator
class Person:
    def __init__(self,name):
        self.name = name
    def say_hello(self):
        print("Hello " + self.name)

a = Person("A")
b = Person("B")

a.say_hello()
b.say_hello()
print(a.name)
print(b.name)

#输出:
Hello A
Hello B
A
B

这个例子使用基于函数的方式(类工厂函数)创建装饰器,可支持多个实例,因为每次使用都会创建一个新的Wrapper对象和和被装饰类实例 。

错误示例:不支持多个实例

class Decorator:
    def __init__(self,cls):
        self.cls = cls
    def __call__(self, *args, **kwargs):
        self.wrapper = self.cls(*args,**kwargs)  #Person实例覆盖上一个实例                                         
        return self
    def __getattr__(self, item):
        return getattr(self.wrapper, item)


@Decorator
class Person:
    def __init__(self,name):
        self.name = name
    def say_hello(self):
        print("Hello " + self.name)

a = Person("A")
b = Person("B")

a.say_hello()   
b.say_hello()
print(a.name)
print(b.name) 

#输出:
Hello B
Hello B
B
B

因为Decorator只有一个实例,所以新的Person实例赋值给sefl.wrapper时,会覆盖上一个实例 。

而上一例中,类似工厂函数,调用被装饰类创建实例时,会调用Wrapper的__init__生成新实例。

2.2重定义运算符重载

因装饰器返回的是一个新类,内置操作需要在装饰器中重新定义运算符重载方法,关联到原始的类。

示例:

def decorator(cls):
    class OnInstance:
        def __init__(self,*args,**kwargs):
            self.wrapped = cls(*args,**kwargs)
        #重定义重载运算符,指向被装饰类
        def __str__(self):
            return str(self.wrapped)
    return OnInstance

@decorator
class Example:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return "my name is " + self.name

e1 = Example("e1")
print(e1)

还可以通过定义一个OnInstance的混入父类(Mixin)实现。 

 3.装饰器嵌套

 装饰器按照被应用的顺序从内到外执行。类似套娃从内到外一层一层套。

@A
@B
@C
def f():
    ...

运行等效于:A(B(C(f)))

示例: 

def decorator1(F):
    def wrapper():
        print('decorator1 before')
        F()
        print('decorator1 after')
    return wrapper

def decorator2(F):
    def wrapper():
        print('decorator2 before')
        F()
        print('decorator2 after')
    return wrapper

def decorator3(F):
    def wrapper():
        print('decorator3 before')
        F()
        print('decorator3 after')
    return wrapper

@decorator1
@decorator2
@decorator3
def say():
    print("yes")

#输出结果:
decorator1 before
decorator2 before
decorator3 before
yes
decorator3 after
decorator2 after
decorator1 after

 4.装饰器参数

装饰器参数通常用来保持状态信息供随后的调用使用。通过创建装饰器工厂函数把参数传给它,并返回一个装饰器。

示例:

def decorator(A,B):
    #保存或使用A,B
    def actual_decorator(F):
        #保存或使用函数F
        def wrapper(*args):
            return F(*args)
        return wrapper
    return actual_decorator



@decorator(A,B)
def F(*arg):
    ...

#等效于
F = decorator(A,B)(*arg)

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值