写在前头,本来不打算写这些东西的,毕竟网上一大堆,官方文档一大堆。可是有些看官和徒弟不懂一些Python知识,讲解Odoo知识时候有些吃力,只能补充一下,写一写基础的。真人、神仙、佛祖级别的就自动略过。   
关于Python的一些秘史啊,我们就扒一扒。那各位看官,就请搬板凳坐一排,且听我细细道来。  
一、前戏  
首先,我们先说对象的事情。各位single dog要爱护自己的两个五菇凉啊!这是最重要的伴侣! Python中就有一个对象的概念。 万物皆可盘,一切皆对象。
万物皆对象 下面就说说函数。理解Python中的函数。函数是用def定义的,本质也是一个对象。
1 2 3 4 def  hello () :    print('单身狗,你好!' ) print(type(hello)) 
 
输出结果:
 
可以看到,是一个function类型(其实还有method类型,下回讲解…)
我们甚至可以将一个函数赋值给一个变量,比如:
1 2 3 4 5 def  single_dog (name)      return  '%s  is  single  dog ' % name  dog = single_dog  # 注意:这里没有使用小括号,也没有传参,因为我们并不是在调用函数,只是引用函数对象 print(single_dog('小明' ))   
 
此时,如果我们删掉旧的single_dog函数,看看会发生什么!
1 2 3 del  single_dogprint(single_dog())   print(dog('小王' ))   
 
我们发现,即使我们删除旧的方法,但是我们之前赋值引用的函数还是正常使用。 继续…
函数中定义函数 接下来,我们来个骚操作。刚才我们只是定义了一个函数,然后被赋值给一个变量。那么我们能不能在函数中再定义一个函数呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name)     def  hobby () :         return  "%s喜欢看小电影"  % name     def  sex () :         return  "%s是基佬"  % name     print(hobby())     print(sex()) single_dog() 
 
发现了什么?无论怎么调用single_dog(), hobby()和sex()是一定会执行的。这也是必然的,毕竟单身狗都好这口,各位看官您说呢? 如果,我们单独调用hobby()或者sex()呢?
 
可见,我们是不能直接调用内部函数的。 那现在我们在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。但是我们不能直接调用内部函数。那我们能否在函数中返回一个函数呢? 继续…
函数中返回一个函数 其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来:
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 def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name)     def  hobby () :         return  "%s喜欢看小电影"  % name     def  sex () :         return  "%s是基佬"  % name     if  name == '小明' :         return  hobby     else :         return  sex dog1 = single_dog() print(dog1) print(dog1()) dog2 = single_dog('小明' ) print(dog2) print(dog2()) 
 
发现了什么?dog1指向了sex函数,dog2指向了hobby函数,并且dog1和dog2都可以加()运行。 由此可见,函数是可以返回一个函数,并且正常的被调用运行的。那么我们定义函数时候,能不能传入一个函数呢? 继续…
依然是前戏。
函数作为参数传入到函数中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name)     def  hobby () :         return  "%s喜欢看小电影"  % name     def  sex () :         return  "%s是基佬"  % name     print(hobby())     print(sex()) def  is_dog (func) :    print('单身狗也是狗' )     func() is_dog(single_dog) 
 
要想做得好,前戏得做足了。说了这么久,还没进入主题。小王说他就蹭蹭不进去,那我们进去。
正主“装饰器” 装饰器?啥是装饰器?装饰器(Decorators)是 Python 的一个重要部分。简单地说:就是在不动其他函数时,给其增加功能进行丰富的函数。有助于让我们的代码更简短,也更Pythonic。 啥?不明白?要一个场景?那行吧…
比如: 果农2块的苹果卖出去后被放入盒子里,最后变成被果农儿子花10块买回来的平安果。
要个实际场景?
场景来了: 小明是个单身狗,喜欢看小电影。在电脑某个盘里存放了一部XDYJ-6961.mp4的文件。以便于夜深人静的时候观赏。但是呢,平时小王又会用他电脑。小明害怕小王发现,就对该文件进行了修饰修饰,把文件放入目录名为人类文明的起源的文件夹下。此时呢,小明晚上仍可以欣赏。这就是做到不动原本的,给其装饰了一番。
要实战姿势?其实我们上面已经写了一个装饰器了。(闭包这些个概念,您就自己百度吧…)
第一个装饰器(函数装饰函数) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def  hobby (func) :    def  wrapfunc () :         print("单身狗喜欢看小电影" )         func()     return  wrapfunc def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name) single_dog() single_dog = hobby(single_dog) single_dog() 
 
这就是一个装饰器,没有改变原函数,再原函数的功能上增加了一个功能。wrapfunc就是一个包装器。那有看官就问“为啥我看到的装饰器都是@开始的呢?” 那是因为@是Python的一个语法糖。上面代码等同于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def  hobby (func) :    def  wraps () :         print("单身狗喜欢看小电影" )         func()     return  wraps @hobby def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name) single_dog() 
 
这就是Pythonic的写法,至于其他的语法糖也请自行百度。
包装器的装饰器–wraps 此时,我们的装饰器是存在一些问题的。我们的被装饰的函数名字到底变成了什么呢?
1 2 3 4 print(single_dog.__name__) 
 
显然,这不是我们想要的。函数名被替换了包装器。它重写了我们函数的名字和注释文档(docstring)。 对于受到封装的原函数来说,装饰器能够在那个函数执行前或者执行后分别运行一些代码,使得可以再装饰器里面访问并修改原函数的参数以及返回值,以实现约束定义、调试程序、注册函数等目标。装饰器一般返回一个包装器(wrapper),而functools.wraps就是装饰包装器的装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from  functools import  wrapsdef  hobby (func) :    @wraps(func)  # 包装器的装饰器     def  wrapfunc () :         print("单身狗喜欢看小电影" )         func()     return  wrapfunc @hobby def  single_dog (name="小王" ) :    print("%s是个单身狗"  % name) print(single_dog.__name__) 
 
wraps保留了原函数的属性
使用场景 授权(Authorization) 装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:
1 2 3 4 5 6 7 8 9 10 from  functools import  wrapsdef  requires_auth (f) :    @wraps(f)     def  decorated (*args, **kwargs) :         auth = request.authorization         if  not  auth or  not  check_auth(auth.username, auth.password):             authenticate()         return  f(*args, **kwargs)     return  decorated 
 
日志(Logging) 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 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() 
 
装饰器类(类装饰函数) 现在我们有了能用于正式环境的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 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) :                  pass  
 
这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:
1 2 3 @logit() def  myfunc1 () :    pass  
 
现在,我们给 logit 创建子类,来添加 email 的功能。
1 2 3 4 5 6 7 8 9 10 11 12 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) :                           pass  
 
从现在起,@email_logit 将会和 @logit 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。
函数装饰类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def  wrapClass (cls) :    def  inner (a) :         print('class name:' , cls.__name__)         return  cls(a)     return  inner @wrapClass class  Foo () :    def  __init__ (self, name) :         self.name = name     def  fun (self) :         print('self.name =' , self.name) Foo('小明' ) 
 
类装饰类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  ShowClassName (object) :    def  __init__ (self, cls) :         self._cls = cls     def  __call__ (self, a) :         print('class name:' , self._cls.__name__)         return  self._cls(a) @ShowClassName class  Foobar (object) :    def  __init__ (self, a) :         self.value = a     def  fun (self) :         print(self.value) Foobar('小明' ) 
 
以上。 都是Python的基础知识,希望各位能够掌握装饰器的基本用法。 – end –
看官,觉得好,就请喝杯咖啡吧!