1 2 from  IPython.core.interactiveshell import  InteractiveShellInteractiveShell.ast_node_interactivity = 'all'  
 
装饰器(Decorators) 装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def  hi (name=" world"  ):    return  "hello"  + name hi()   greet = hi greet() del  hi  greet() 
 
1 2 3 4 5 'hello world' 'hello world' 'hello world' 
 
在函数中定义函数 刚才那些就是函数的基本知识了。我们来让你的知识更进一步。在 Python 中我们可以在一个函数中定义另一个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def  testfunction (parameter="info"  ):    print ("现在会调用里层函数" )       def  greet ():         return  "greet函数被调用"        def  welcome ():         return  "welcome函数被调用"           def  gentleman ():         print ("gentleman函数被调用" )     print (greet())     print (welcome())     gentleman()     print ("函数调用结束" )   testfunction()   
 
1 2 3 4 5 现在会调用里层函数 greet函数被调用 welcome函数被调用 gentleman函数被调用 函数调用结束 
 
可以在函数中定义另外的函数。也就是说可以创建嵌套的函数。现在需要再多学一点,就是函数也能返回函数。
从函数中返回函数 其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来:  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def  testfunction (parameter="info"  ):    def  greet ():         return  "greet函数被调用"        def  welcome ():         return  "welcome函数被调用"      if  parameter == "info" :         return  greet     else :         return  welcome a = testfunction() print (a)print (a())testfunction()() 
 
1 2 3 4 <function  testfunction.<locals>.greet at 0x7f9e00638f28> greet函数被调用 'greet函数被调用' 
 
再次看看这个代码。在if/else语句中返回greet和welcome,而不是greet()和welcome()。 当把一对小括号放在后面,这个函数就会执行;然而如果不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。 再稍微多解释点细节: 当我们写下a = testfunction(),testfunction()会被执行,而由于name参数默认是info,所以函数greet被返回了。如果我们把语句改为a = testfunction(name = “Alice”),那么welcome函数将被返回。 我们还可以打印出testfunction()(),这会输出greet函数被调用。
将函数作为参数传给另一个函数 1 2 3 4 5 6 def  testfunction ():    return  "hello world!"  def  Do_Something_Before_testfunction (func ):    print ("I am doing some boring work before executing hi()" )     print (func()) 
 
1 2 3 Do_Something_Before_testfunction(testfunction) I am doing some boring work before executing hi() hello world! 
 
现在已经具备所有必需知识来学习装饰器是什么了,装饰器让你在一个函数的前后去执行代码。
装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def  function1 (a_func ):    def  wrapTheFunction ():         print ("在执行a_func()之前我正在做一些无聊的工作" )         a_func()         print ("在执行a_func()之后我正在做一些无聊的工作" )     return  wrapTheFunction def  function2 ():    print ("函数2的输出" ) function2() function2 = function1(function2) function2() 
 
1 2 3 4 5 6 函数2的输出 在执行a_func()之前我正在做一些无聊的工作 函数2的输出 在执行a_func()之后我正在做一些无聊的工作 刚刚应用了之前学习到的原理。这正是python中装饰器做的事情!它封装一个函数,并且用这样或者那样的方式来修改它的行为。 现在也许疑惑,在代码里并没有使用@符号?只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用@来运行之前的代码: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 @function1 def  a_function_requiring_decoration ():    """Hey you! Decorate me!"""      print ("I am the function which needs some decoration to "            "remove my foul smell" ) a_function_requiring_decoration() a_function_requiring_decoration = function1(a_function_requiring_decoration) 
 
1 2 3 在执行a_func()之前我正在做一些无聊的工作 I am the function  which  needs some decoration to remove my foul smell 在执行a_func()之后我正在做一些无聊的工作 
 
希望现在对Python装饰器的工作原理有一个基本的理解。如果运行如下代码会存在一个问题:
1 2 print (a_function_requiring_decoration.__name__)
 
 
这并不是想要的!Ouput输出应该是a_function_requiring_decoration。这里的函数被warpTheFunction替代了。它重写了函数的名字和注释文档(docstring)。幸运的是Python提供给一个简单的函数来解决这个问题,那就是functools.wraps。修改上一个例子来使用functools.wraps:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from  functools import  wrapsdef  a_new_decorator (a_func ):    @wraps(a_func )     def  wrapTheFunction ():         print ("I am doing some boring work before executing a_func()" )         a_func()         print ("I am doing some boring work after executing a_func()" )     return  wrapTheFunction @a_new_decorator def  a_function_requiring_decoration ():    """Hey yo! Decorate me!"""      print ("I am the function which needs some decoration to "            "remove my foul smell" ) print (a_function_requiring_decoration.__name__)
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 a_function_requiring_decoration from  functools import  wrapsdef  decorator_name (f ):    @wraps(f )     def  decorated (*args, **kwargs ):         if  not  can_run:             return  "Function will not run"          return  f(*args, **kwargs)     return  decorated @decorator_name def  func ():    return ("Function is running" ) can_run = True  print (func())can_run = False  print (func())
 
1 2 Function is running Function will not run 
 
注意: @wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。 装饰器在某些地方特别耀眼,使用它可以让一些事情管理起来变得更简单。 装饰器可以用于日志管理 装饰器可以监控函数运行之前的状态,捕捉函数的运行情况,传入参数等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from  functools import  wrapsdef  logit (func ):    @wraps(func )     def  with_logging (*args, **kwargs ):         print (func.__name__ + " was called" )         return  func(*args, **kwargs)     return  with_logging @logit def  addition_func (x ):   """Do some math."""     return  x + x result = addition_func(4 ) 
 
1 addition_func was called 
 
带参数的装饰器 来想想这个问题,难道@wraps不也是个装饰器吗? 但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢? 这是因为,当使用@my_decorator语法时,是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数! 记住了这些,我们可以编写一个能返回一个包裹函数的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from  functools import  wrapsdef  logit (logfile='out.log'  ):    def  logging_decorator (func ):         @wraps(func )         def  wrapped_function (*args, **kwargs ):             log_string = func.__name__ + " was called"              print (log_string)                          with  open (logfile, 'a' ) as  opened_file:                                  opened_file.write(log_string + '\n' )             return  func(*args, **kwargs)         return  wrapped_function     return  logging_decorator @logit() def  myfunc1 ():    pass  myfunc1() @logit(logfile='func2.log'  ) def  myfunc2 ():    pass  myfunc2() 
 
1 2 myfunc1 was called myfunc2 was called 
 
装饰器类 现在有了能用于正式环境的logit装饰器,但当应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时想打日志到一个文件。而有时想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止只看到过用来构建装饰器的函数。 幸运的是,类也可以用来构建装饰器。那现在以一个类而不是一个函数的方式,来重新构建logit。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from  functools import  wrapsclass  logit (object ):    def  __init__ (self, logfile='out.log'  ):         self.logfile = logfile     def  __call__ (self, func ):         @wraps(func )         def  wrapped_function (*args, **kwargs ):             log_string = func.__name__ + " was called"              print (log_string)                          with  open (self.logfile, 'a' ) as  opened_file:                                  opened_file.write(log_string + '\n' )                          self.notify()             return  func(*args, **kwargs)         return  wrapped_function     def  notify (self ):                  print ("打印日志" )         pass  
 
这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:
1 2 3 4 5 @logit() def  myfunc1 ():    print ("开始" ) myfunc1() 
 
1 2 3 myfunc1 was called 打印日志 开始 
 
现在,我们给logit创建子类,来添加新的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  email_logit (logit ):    '''      一个logit的实现版本,可以在函数调用时发送email给管理员     '''     def  __init__ (self, email='admin@myproject.com' , *args, **kwargs ):         self.email = email         super (email_logit, self).__init__(*args, **kwargs)     def  notify (self ):                           print ("假设已经发送了邮件" ) @email_logit() def  myfunc1 ():    print ("开始" ) myfunc1() 
 
1 2 3 myfunc1 was called 假设已经发送了邮件 开始