python装饰器理解.md

函数装饰器

先来看个例子:

def foo(*args, **kwargs):
    print 'args = ', args
    print 'kwargs = ', kwargs
    print '---------------------------------------'

if __name__ == '__main__':
    foo(1,2,3,4)
    foo(a=1,b=2,c=3)
    foo(1,2,3,4, a=1,b=2,c=3)
    foo('a', 1, None, a=1, b='2', c=3)
输出结果如下:
args =  (1, 2, 3, 4)
kwargs =  {}
---------------------------------------
args =  ()
kwargs =  {'a': 1, 'c': 3, 'b': 2}
---------------------------------------
args =  (1, 2, 3, 4)
kwargs =  {'a': 1, 'c': 3, 'b': 2}
---------------------------------------
args =  ('a', 1, None)
kwargs =  {'a': 1, 'c': 3, 'b': '2'}
---------------------------------------

可以看到,这两个是python中的可变参数。args表示任何多个无名参数,它是一个tuple;**kwargs表示关键字参数,它是一个dict。并且同时使用args和kwargs时,必须*args参数列要在kwargs前,像foo(a=1, b=‘2’, c=3, a’, 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。

def use_logging(func):
    def wrapper(*args,**kwargs):
        logging.warning("%s is running" % func.__name__)
        return func(*args)
    return wrapper

@use_logging
def foo():
    print("i am foo")


@use_logging
def bar():
    print('i am bar')


# bar=use_logging(bar)
bar()
foo()

这里的大概逻辑:
有个功能(logging.warning),两个函数foo和bar都会用到;
但是又不想多写,于是写成下面形式了:
在调用foo的时候,执行了logging.warning这个功能;
在调用bar的时候,也执行了logging.warning这个功能;
但是只写了一遍!

def use_logging(level):
    def decorateor(func):
        def wrapper(*args, **kwargs):
            if level =="warn":
               logging.warning("%s is running" % func.__name__)
            else:
                print("hello !")
            return func(*args)

        return wrapper
    return decorateor


@use_logging(level="warn")
def foo(name="foo"):
    print("i am %s"%name)



@use_logging(level="hello")
def bar(name="bar"):
    print('i am %s'%name)


foo()
bar()

带参数的装饰器:
大体逻辑同上;
use_logging是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。
我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level=“warn”)调用的时候,
Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

类装饰器


class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('class decorator runing')
        self._func()
        print('class decorator ending')

@Foo
def bar():
    print ('bar')

@Foo
def hi():
    print("hi")

bar()
hi()

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

缺点

缺点:
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,
比如函数的docstring、name、参数列表,先看例子:

def logged(func):
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging



@logged
def f(x):
    return x+x*x
f = logged(f)

"""
with_logging was called
f was called
"""





def f(x):
    return x+x*x
f = logged(f)
"""
f was called

"""

f(2)

这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。



from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
    print("this is f(x)")
    return x + x * x
f(2)

print("=================================")
print(f.__name__)
print("=================================")
print(f.__doc__)

装饰器的顺序

@a
@b
@c
def f ():

等效于:

f = a(b(c(f)))

当装饰器也需要参入参数时我们需要给装饰器再加一层函数,
此时装饰器接受到的方法需要进入第二层函数进行接受,
第一层需要接受装饰器自己的参数

user, password = 'db', '12345'

def login(login_type):
    def outer_wrapper(func):
        def wrapper(*agr1, **kwargs):
            usernameInput = input("UserName:").strip()
            passwordInput = input("Password:").strip()
            if login_type == "local":
                if user == usernameInput and password == passwordInput:
                    print("login successful")
                    res = func(*agr1, **kwargs)  # 接受返回结果
                    return res
                else:
                    print("login fail")
            elif login_type == "ldap":
                print("远程登录")

        return wrapper

    return outer_wrapper

def index():
    print("welcome to index page")


@login(login_type="local")  # 对装饰分类
def home():
    print("welcome to home page")
    return "from home"


@login(login_type="ldap")  # 对装饰分类
def blog():
    print("welcome to blog page")


index()
print(home())
blog()

参考

https://blog.csdn.net/qq_38520096/article/details/79254901

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值