python函数装饰器

装饰器(Decorators)是Python的一个重要部分。简单地说:他们是修改其它函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic。

一切皆对象

我们可以将一个函数赋值给一个变量

def hi():
    print("hihi")
a=hi
print(a,type(a))
hi() #调用hi函数,需要加()

 运行结果

 

 在函数中定义函数

def hi():
    print("hihi")
    def yun():
        return "yunyun"
    print(yun())
hi()
#yun()#报错#在hi()函数之外是不能访问的

 运行结果

 

 从函数中返回函数

def hi(name="xx"):
    print(name)

    def he():
        return "xixi"
    return he #返回he相当于返回he()函数
a=hi() #此时不会调用he(),因为hi()函数中无调用此函数的语句
print(a,type(a))
print(a()) #函数调用需要加()  

 运行结果

 

将函数作为参数传给另一个函数

 

def hi(name="xx"):
    print(name)

def study(a):
    a()
    print("我是study")

study(hi)

 运行结果

 

 第一个装饰器

下面的代码你看的懂?都是上面刚刚提到的知识点。

def aa(bb):
    print("我是aa哈")
    def cc():
        print("我是cc吆")
        bb()
        return "cc"
    return cc

def xixi():
    print("我是xixi")

m=aa(xixi) #输出:我是aa哈,
print(m,type(m)) #m是cc()函数
print(m()) #我是cc吆,我是xixi,cc函数的返回值  

 运行结果

  

 下面我们使用@来修改上面的代码

def aa(bb):
    print("我是aa哈")
    def cc():
        print("我是cc吆")
        bb()
        return "cc"
    return cc
@aa
def xixi():
    print("我是xixi")

print(xixi()) #等价于aa(xixi)()  

 运行结果

 

下面存在一个问题

print(xixi.__name__) #输出cc  

 这并不是我们想要的!应该输出xixi。这里的函数被cc替代了。它重写了我们函数的名字和注释文档(doctring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是Functools.wraps。我们使用functools.wraps修改上一个例子

from functools import wraps
def aa(bb):
    print("我是aa哈")
    @wraps(bb)
    def cc():
        print("我是cc吆")
        bb()
        return "cc"
    return cc
@aa
def xixi():
    print("我是xixi")

#print(xixi()) #等价于aa(xixi)()
print(xixi.__name__)  

 运行结果

 

 注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

蓝本规范:

from functools import wraps
def aa(bb):
    print("我是aa哈")
    @wraps(bb)
    def cc(*args,**kwargs):
        if not can_run:
            return "bb方法不运行"
        return bb(*args,**kwargs)
    return cc
@aa
def xixi():
    return "我是xixi"

can_run=False
print(xixi()) #我是aa哈;bb方法不运行;

# can_run=True
# print(xixi()) #我是aa哈;我是xixi

使用场景

  授权(Authorization)

  日志(Logging)

 带参数的装饰器

来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢?这是因为,当你使用@aa语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!

在函数中嵌入装饰器

from functools import wraps
def aa(name="xiaoxiao"):
    def bb(cc):
        @wraps(cc)
        def dd(*args,**kwargs):
            log=cc.__name__+"被调用"
            print(log)
            return cc(*args,**kwargs)
        return dd
    return bb
# @aa() #注意与无参的对比,这里多了()
# def xixi():
#     return "我是xixi"

@aa(name="dehua")
def xixi():
    return "我是xixi"
print(xixi()) #xixi被调用,我是xixi

 运行结果

装饰器类

没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
    def __init__(self, func):
        self._func = func
        #print(self._func)
    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')
@Foo
def bar():
    print ('bar')
bar()  

 运行结果

参考:https://www.runoob.com/w3cnote/python-func-decorators.html

   https://www.runoob.com/w3cnote/python-func-decorators.html

  

posted on 2020-08-02 22:54  星空6  阅读(236)  评论(0编辑  收藏  举报

导航