写在前头,本来不打算写这些东西的,毕竟网上一大堆,官方文档一大堆。可是有些看官和徒弟不懂一些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 –
看官,觉得好,就请喝杯咖啡吧!