久久不用,发现又忘了修饰器是怎么用的了。记在博客里会妥妥点。

一、 什么是修饰器:

1、以我的理解来说,修饰器就是为了给被修饰的函数进行预处理。什么是预处理呢?

比如说下面这个例子:

def myDeco(func):
    print("Hello ,I'm Decorator!")
    return func

@myDeco
def run():
    print("func:run")

run()
def myDeco(func):
    print("Hello ,I'm Decorator!")
    return func

@myDeco
def run():
    print("func:run")

run()

输出结果 :


ubuntu@yee:/tmp$ python test.py 
Hello ,I'm Decorator!
func:run
ubuntu@yee:/tmp$ python test.py 
Hello ,I'm Decorator!
func:run

可以看到,在执行run之前,先经过了myDeco来进行一些预处理。具体在现实中是如何运用的,下文再说。


处理参数 

def myDeco(func):
    print("Hello ,I'm Decorator!")
    def _myDeco(*args,**kwargs): //被修饰的函数的参数 
        print("my name:%s",func.__name__)
        ret = func(*args,**kwargs)
        return ret
    return _myDeco

@myDeco
def run(a,b):
    print("func run start:")
    print("---------------")
    print("run(%s,%s)" % (a,b))
    print("---------------")
    print("func run end")


run(1,2)
def myDeco(func):
    print("Hello ,I'm Decorator!")
    def _myDeco(*args,**kwargs): //被修饰的函数的参数 
        print("my name:%s",func.__name__)
        ret = func(*args,**kwargs)
        return ret
    return _myDeco

@myDeco
def run(a,b):
    print("func run start:")
    print("---------------")
    print("run(%s,%s)" % (a,b))
    print("---------------")
    print("func run end")


run(1,2)

3、带参数的修饰器:

def myDeco(args):
    print("Hello ,I'm Decorator!")
    print("args:",args) //处理参数 
    def _myDeco(func)://处理函数 
        def __myDeco(*args,**kwargs): //处理函数的参数 
            print("my name:%s",func.__name__)
            ret = func(*args,**kwargs)
            return ret
        return __myDeco
    return _myDeco

@myDeco("my_name")
def run(a,b):
    print("func run start:")
    print("---------------")
    print("run(%s,%s)" % (a,b))
    print("---------------")
    print("func run end")


run(1,2)
def myDeco(args):
    print("Hello ,I'm Decorator!")
    print("args:",args) //处理参数 
    def _myDeco(func)://处理函数 
        def __myDeco(*args,**kwargs): //处理函数的参数 
            print("my name:%s",func.__name__)
            ret = func(*args,**kwargs)
            return ret
        return __myDeco
    return _myDeco

@myDeco("my_name")
def run(a,b):
    print("func run start:")
    print("---------------")
    print("run(%s,%s)" % (a,b))
    print("---------------")
    print("func run end")


run(1,2)

其它的还有“带类参数的修饰器”等一些高级特性,不过我没用过,就不记了。

二、具体应用 :

了解一些语法功能之后,总是需要知道它的应用场景吧,不然就没有用处了。

我以 tornado 这个web框架为例来说一下它的具体使用场景 。

先看代码就明白了:

class MessageNewHandler(BaseHandler,MessageMixin)://聊天室中的新消息处理,接收新消息
    @tornado.web.authenticated                    //必须经过登录认证
    def post(self):
        user  = self.get_current_user()
        message = {
                "id":str(uuid.uuid4()),
                "current_user":user.id,
                'up':0,
        }
class MessageNewHandler(BaseHandler,MessageMixin)://聊天室中的新消息处理,接收新消息
    @tornado.web.authenticated                    //必须经过登录认证
    def post(self):
        user  = self.get_current_user()
        message = {
                "id":str(uuid.uuid4()),
                "current_user":user.id,
                'up':0,
        }

上面的代码已经很明确了,在聊天室中的用户 post一条新消息之前,tornado会先检查用户是否已经登录认证了,只有经过认证的用户才可以post消息。


看看tornado.web.authenticated 是怎么实现的:

def authenticated(method):
    """Decorate methods with this to require that the user be logged in."""
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
            if self.request.method in ("GET", "HEAD"):
                url = self.get_login_url()
                if "?" not in url:
                    if urlparse.urlsplit(url).scheme:
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
                        next_url = self.request.uri
                    url += "?" + urllib.urlencode(dict(next=next_url))
                self.redirect(url)
                return
            raise HTTPError(403)
        return method(self, *args, **kwargs)
    return wrapper
def authenticated(method):
    """Decorate methods with this to require that the user be logged in."""
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
            if self.request.method in ("GET", "HEAD"):
                url = self.get_login_url()
                if "?" not in url:
                    if urlparse.urlsplit(url).scheme:
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
                        next_url = self.request.uri
                    url += "?" + urllib.urlencode(dict(next=next_url))
                self.redirect(url)
                return
            raise HTTPError(403)
        return method(self, *args, **kwargs)
    return wrapper

---------------------------------------------------------------

简单讲一下 functools.wraps 这个修饰器的作用:


update_wrapper,wraps ,而wraps 只是对 update_wrapper进行了封装一下而已。

在修饰器

def myDeco(func)

    return func
def myDeco(func)

    return func

这一句中,func 实际上已经丢掉了原func 的几个属性:__name__、__module__、__doc__和 __dict__,所以,返回后的函数你无法再使用  func.__doc__  来获得注释内容 ,而如果改成这样:

def myDeco(func):

    @functools.wraps(func)

    def _myDeco(*args,**kwargs):

        return func(*args,**kwargs)

    return _myDeco
def myDeco(func):

    @functools.wraps(func)

    def _myDeco(*args,**kwargs):

        return func(*args,**kwargs)

    return _myDeco

则 functools.wraps 会帮你重新绑定在返回的新函数上。

---------------------------------------------------------------

说回到tornado 的例子,看它是怎么做认证 预处理的。

首先是:

if not self.current_user
if not self.current_user

判断是否当前用户(self.current_user是tornado的内置变量,保存当前登录的用户),如果不是,则抛出错误:

raise HTTPError(403)
raise HTTPError(403)

否则就返回 :


return method(self, *args, **kwargs)
return method(self, *args, **kwargs)

表示认证成功,开发者可以继续对认证成功的用户做应该做的动作。


这样子,python的修饰器如何应用 应该都明白 了。