因为不是专职python开发,不需要多人合作,也没有写过特别大型的代码。装饰器、描述器、闭包的概念工作中一直用不上。
所以一直不太想学习相关的内容,因为这些内容太不C了,只是一种额外的写法而已。但是这两天面试的时候别人问了个闭包导致我直接跪了以后,痛定思痛,捡起来算了,反正以后无论是面试还是工作都要用,花1天学习一下也好。
我先来看装饰器,先来看一个最常见的装饰器写法
def a(func):
print func.__name__
def b():
print 'decorator!!!!!'
func()
return b
@a
def test():
pass
@a
def test2():
pass
前面的装饰器比较简单,装饰的函数不能接收参数,我们再来一个常见的装饰器的例子
import functools
def delayer(seconds):
"""
seconds: 延迟的秒数
装饰器用于延迟执行函数
"""
def wrapper(func):
# @functools.wraps(func)
def _wrapper(*args, **kwargs):
_start = time.time()
time.sleep(seconds)
ret = func(*args, **kwargs)
print 'time use:', time.time() - _start
return ret
return _wrapper
return wrapper
@delayer(seconds=3)
def test(a):
print a
return 'bbb'
new_function = test
print new_function.__name__
new_function('lalala')
注释那一行和 “print new_function” 部分有关,自己可以试试放开注释有什么效果,具体可以参考这里。
解释下为什么嵌套三层
第一层, 接受的参数seconds,延迟时间,无此参数可以少一层嵌套
第二层, 接受的参数的func就是外部函数,也就是需要"装饰"的函数
第三层, 接收的参数就是func所接收的参数,func无参数也可以少一层嵌套
如果不用装饰器的写法@delayer(3)来实现,那上述new_function(‘lalala’)就要这样写
delayer(3)(test)('lalala')
这里就明白装饰器的实际作用了,装饰器只是一个语法糖,简化了套娃一样的写法而已
这里的装饰器上的delayer是函数, 这种装饰器就是函数装饰
函数装饰器就会牵扯到闭包这个概念,当delayer中有复杂内容时候,具体参考python 闭包
如果装饰器不是一个函数而是一个类的话,那么装饰器就是类装饰器
例如
class Descriptor(object):
def __init__(self, func):
pass
原理都是一样,套娃语法糖,如果需要传入参数,需要给类增加__call__方法
class Descriptor(object):
def __init__(self, arg):
pass
def __call__(self, func):
pass
类装饰器一般用于装饰类中的方法,原理会涉及到python 描述器
最后我们来段opentack的代码,这个函数装饰器用来限定只调用一起启动。要么用eventlet模式要么用stdlib模式,这里也算是python的单列模式实现方法
def configure_once(name):
"""Ensure that environment configuration is only run once.
If environment is reconfigured in the same way then it is ignored.
It is an error to attempt to reconfigure environment in a different way.
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
global _configured
if _configured:
if _configured == name:
return
else:
raise SystemError("Environment has already been "
"configured as %s" % _configured)
LOG.debug("Environment configured as: %s", name)
_configured = name
return func(*args, **kwargs)
return wrapper
@configure_once('eventlet')
def use_eventlet(monkeypatch_thread=None):
global httplib, subprocess, Server
...
@configure_once('stdlib')
def use_stdlib():
global httplib, subprocess
...