zoukankan      html  css  js  c++  java
  • python 装饰器

    原文:http://docs.pythontab.com/interpy/decorators/your_first_decorator/

    你的第一个装饰器:

    在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序:

     1 def a_new_decorator(a_func):
     2 
     3     def wrapTheFunction():
     4         print("I am doing some boring work before executing a_func()")
     5 
     6         a_func()
     7 
     8         print("I am doing some boring work after executing a_func()")
     9 
    10     return wrapTheFunction
    11 
    12 def a_function_requiring_decoration():
    13     print("I am the function which needs some decoration to remove my foul smell")
    14 
    15 a_function_requiring_decoration()
    16 #outputs: "I am the function which needs some decoration to remove my foul smell"
    17 
    18 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
    19 #now a_function_requiring_decoration is wrapped by wrapTheFunction()
    20 
    21 a_function_requiring_decoration()
    22 #outputs:I am doing some boring work before executing a_func()
    23 #        I am the function which needs some decoration to remove my foul smell
    24 #        I am doing some boring work after executing a_func()

    你看明白了吗?我们刚刚应用了之前学习到的原理。这正是python中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用@符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用@来运行之前的代码:

     1 @a_new_decorator
     2 def a_function_requiring_decoration():
     3     """Hey you! Decorate me!"""
     4     print("I am the function which needs some decoration to "
     5           "remove my foul smell")
     6 
     7 a_function_requiring_decoration()
     8 #outputs: I am doing some boring work before executing a_func()
     9 #         I am the function which needs some decoration to remove my foul smell
    10 #         I am doing some boring work after executing a_func()
    11 
    12 #the @a_new_decorator is just a short way of saying:
    13 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

    希望你现在对Python装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题:

    1 print(a_function_requiring_decoration.__name__)
    2 # Output: wrapTheFunction

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

     1 from functools import wraps
     2 
     3 def a_new_decorator(a_func):
     4     @wraps(a_func)
     5     def wrapTheFunction():
     6         print("I am doing some boring work before executing a_func()")
     7         a_func()
     8         print("I am doing some boring work after executing a_func()")
     9     return wrapTheFunction
    10 
    11 @a_new_decorator
    12 def a_function_requiring_decoration():
    13     """Hey yo! Decorate me!"""
    14     print("I am the function which needs some decoration to "
    15           "remove my foul smell")
    16 
    17 print(a_function_requiring_decoration.__name__)
    18 # Output: a_function_requiring_decoration

    现在好多了。我们接下来学习装饰器的一些常用场景。

    蓝本规范:

     1 from functools import wraps
     2 def decorator_name(f):
     3     @wraps(f)
     4     def decorated(*args, **kwargs):
     5         if not can_run:
     6             return "Function will not run"
     7         return f(*args, **kwargs)
     8     return decorated
     9 
    10 @decorator_name
    11 def func():
    12     return("Function is running")
    13 
    14 can_run = True
    15 print(func())
    16 # Output: Function is running
    17 
    18 can_run = False
    19 print(func())
    20 # Output: Function will not run

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

    使用场景

    现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

    授权(Authorization)

    装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

     1 from functools import wraps
     2 
     3 def requires_auth(f):
     4     @wraps(f)
     5     def decorated(*args, **kwargs):
     6         auth = request.authorization
     7         if not auth or not check_auth(auth.username, auth.password):
     8             authenticate()
     9         return f(*args, **kwargs)
    10     return decorated

    日志(Logging)

    日志(Logging)日志是装饰器运用的另一个亮点。这是个例子:

     1 from functools import wraps
     2 
     3 def logit(func):
     4     @wraps(func)
     5     def with_logging(*args, **kwargs):
     6         print(func.__name__ + " was called")
     7         return func(*args, **kwargs)
     8     return with_logging
     9 
    10 @logit
    11 def addition_func(x):
    12    """Do some math."""
    13    return x + x
    14 
    15 
    16 result = addition_func(4)
    17 # Output: addition_func was called

    带参数的装饰器

    来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢?

    这是因为,当你使用@my_decorator语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!记住了这些,我们可以编写一下能返回一个包裹函数的函数。

    在函数中嵌入装饰器

    我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。

     1 from functools import wraps
     2 
     3 def logit(logfile='out.log'):
     4     def logging_decorator(func):
     5         @wraps(func)
     6         def wrapped_function(*args, **kwargs):
     7             log_string = func.__name__ + " was called"
     8             print(log_string)
     9             # 打开logfile,并写入内容
    10             with open(logfile, 'a') as opened_file:
    11                 # 现在将日志打到指定的logfile
    12                 opened_file.write(log_string + '
    ')
    13             return func(*args, **kwargs)
    14         return wrapped_function
    15     return logging_decorator
    16 
    17 @logit()
    18 def myfunc1():
    19     pass
    20 
    21 myfunc1()
    22 # Output: myfunc1 was called
    23 # 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
    24 
    25 @logit(logfile='func2.log')
    26 def myfunc2():
    27     pass
    28 
    29 myfunc2()
    30 # Output: myfunc2 was called
    31 # 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

    装饰器类

    现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。

    幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit

     1 from functools import wraps
     2 
     3 class logit(object):
     4     def __init__(self, logfile='out.log'):
     5         self.logfile = logfile
     6 
     7     def __call__(self, func):
     8         @wraps(func)
     9         def wrapped_function(*args, **kwargs):
    10             log_string = func.__name__ + " was called"
    11             print(log_string)
    12             # 打开logfile并写入
    13             with open(self.logfile, 'a') as opened_file:
    14                 # 现在将日志打到指定的文件
    15                 opened_file.write(log_string + '
    ')
    16             # 现在,发送一个通知
    17             self.notify()
    18             return func(*args, **kwargs)
    19         return wrapped_function
    20 
    21     def notify(self):
    22         # logit只打日志,不做别的
    23         pass

    这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

    1 @logit()
    2 def myfunc1():
    3     pass

    现在,我们给logit创建子类,来添加email的功能(虽然email这个话题不会在这里展开)。

     1 class email_logit(logit):
     2     '''
     3     一个logit的实现版本,可以在函数调用时发送email给管理员
     4     '''
     5     def __init__(self, email='admin@myproject.com', *args, **kwargs):
     6         self.email = email
     7         super(logit, self).__init__(*args, **kwargs)
     8 
     9     def notify(self):
    10         # 发送一封email到self.email
    11         # 这里就不做实现了
    12         pass

    从现在起,@email_logit将会和@logit产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

  • 相关阅读:
    快照原理及场景
    CEP实时分析模型
    请求响应模式
    JMS消息服务模型
    EMF与GEF
    基于SOA的编程模型
    实时计算CEP
    数据库常见的场景
    自签证书服务加入证书验证
    post提交主订单数据(gateway)实现httpapi
  • 原文地址:https://www.cnblogs.com/guolei2570/p/8806321.html
Copyright © 2011-2022 走看看