python深入学习--decorator强大的装饰器


一.decorator基础。

最初接触python是在大学毕业进入某一游戏公司后,最开始觉得python不严谨,很不喜欢python这种“简单”的脚本,但是后来,越来越离不开python了,觉得这么灵活方便的语言,简直是程序员的福音,尤其是它的数据结构,他带来了一个“{}” 大括号就能搞定一切的时代。当然python还有很多优秀的特性。现在我们来讲python的特性之一--decorator(装饰器)。

 

装饰器,顾名思义就是修饰函数或者类的,它把要装饰的函数作为参数,然后在函数体内可以执行很多灵活的操作。这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。装饰器的语法以@开头,接着是装饰器函数的名字和可选的参数。跟着装饰器声明的是被修饰的函数,和装饰函数的可选参数。装饰器看起来会是这样:

 

@decorator()
def func(*args):
	pass

func(*args)

 

其解释器会解释成下面这样的语句:

 

func = decorator(func)
func(*args)
其实,写过python的人都接触过@decorator。比如写一个单例类:

 

 

class Singleton(object):
	_instance = None
	def __init__(self):
		print "init single"
	
	@classmethod
	def instance(cls):
		if cls._instance is None:
			cls._instance = Singleton()
		return cls._instance
	
	def func(self):
		print "call func"

@classmethod就是一个decorator,是python内置的装饰器(还有@staticmethod...)

 

 

class Singleton(object):
	_instance = None
	def __init__(self):
		print "init single"
	
	#@classmethod
	def instance(cls):
		if cls._instance is None:
			cls._instance = Singleton()
		return cls._instance
	
	instance = classmethod(instance)
	
	def func(self):
		print "call func"
	
Singleton.instance().func()

以上跟这个效果是一样的。只不过却让instance少写了两次。

 

 

二.decorator的用法。

1.修饰函数:

decorator修饰函数有几种用法。

1).单个Decorator,不带参数

 

def deco(func):
	def call_method(*args):
		print "before call method"
		func(*args)
		print "after call method"
	return call_method

@deco()
def func(a,b):
	print "in func"
	return a + b

func(1,2)

 

输出为:

before call method
in func
after call method

 

以上对于func的调用 跟以下是一样的(注释掉@deco()):

 

func = deco(func)
func(1,2)

再写的明白点:

call_method = deco(func) #deco这个函数返回call_method

call_method(*args) # 是不是恍然大悟了。

当然,最初的写法是最简单的,它让你每一个调用func()的地方都自动调用了@deco(),然后执行一些额外的操作。比如 print "after call method".当然你可以做你想做的任何事,比如在这里记log,你就不需要边CTRL+C,CTRL+V在每一个调用func的地方加上相同的代码了。

 

2).单个decorator,带参数。

 

def deco(a):
	def real_deco(func):
		def call_method(*args):
			print "before call method"
			result = a + func(*args)
			print "after call method ",result
		return call_method
	return real_deco

@deco("Hello ")
def func(name):
	print "in func ",name
	return name

func("World!")
func("No World!")
输出为:

 

before call method
in func World!
after call method Hello World!


before call method
in func No World!
after call method Hello No World!

 

总结:

无参数decorator:把函数名传进去,然后生成一个新的装饰器函数

有参decorator:有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰

 

3).decorator还可以有多个,如下图所示:

@dec_a
@dec_b
@dec_c
def method(args):
    pass
print "=========="
method = dec_a(dec_b(dec_c(method)))

只不过,平时项目中极少用到,我也就不讲了。

 

那么什么是装饰器?
现在我们知道装饰器实际就是函数。我们也知道他们接受函数对象。但它们是怎样处理那些函数的呢?一般说来,当你包装一个函数的时候,你最终会调用它。最棒的是我们能在包装的环境下在合适的时机调用它。我们在执行函数之前,可以运行些预备代码,也可以在执行代码之后做些清理工作。所以当你看见一个装饰器函数的时候,很可能在里面找到这样一些代码,它定义了某个函数并在定义内的某处嵌入了对目标函数的调用或者至少一些引用。从本质上看,这些特征引入了java 开发者称呼之为AOP(Aspect Oriented Programming,面向方面编程)的概念。
你可以考虑在装饰器中置入通用功能的代码来降低程序复杂度。例如,可以用装饰器来:

引入日志

增加计时逻辑来检测性能

给函数加入事务的能力

 

2.修饰类:

修饰类和修饰函数差不多,修饰类是把@decorator放在class上面,用以修改类的属性

def func(self):
	return "func"

def deco(kclass):
	kclass.func = func
	kclass.name = "Hello"
	return kclass

@deco
class test(object):
	def __init__(self):
		print "in init",self.name
		print "call func: ",self.func()

test()

输出:

 

in init Hello
call func: func

 

三.项目实践

1.最近项目中用到decorator比较多。其中有修饰类的:

 

def is_persistent(_self):
	return True

def Persistent(klass):
	"""
	类的decorator,用来修饰Entity的子类。如:
	@Persistent
	class player(Entity):
		...
	这样的类才会被序列化到mongodb中
	"""
	klass.is_persistent = is_persistent
	return klass

@Persistent
class player(Entity):
	pass
相当于在 player里面定义了一个 is_persistent()的方法。在需要Persistent的地方加上@Persistent就行了,当然也可以在每个需要@Persistent的类里面重写这个方法,但是当对象多的时候,自己也不知道自己写了没有了。

 

 

2.用在callback里面,但是callback的参数有的存在,有的还需要经过复杂的操作获取,假如你要经过一个异步的操作,操作回来调用func(requestId,*args),requestId是已知的,但是*args是回调回来才有的,你肯定有地方可以存requestId,然后回调回来,再取值进行func操作,但是使用类似decorator的操作可以简化这一操作:

 

def request_decorator(requestId, func):
	"""
	传递请求序号参数,用于回调
	:param func:
	:return:
	"""
	def requestId_callback(*args):
		return func(requestId, *args)
	return requestId_callback

callback = request_decorator(requestId, self.call_func)
...
args = ...
callback(*args)

巧妙的把已有的参数传进去,然后等其他参数回来就可以了。

 


 

相关内容

    暂无相关文章

评论关闭