ITKeyword,专注技术干货聚合推荐

注册 | 登录

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

majianfei1023 分享于 2015-04-14

推荐:深入学习python(三) 闭包(Decorator)与装饰器(Closure)

这一章,先寄在这里,有机会再来丰富这章。 有几篇文章非常适合了解。 点击打开链接 点击打开链接 点击打开链接

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

欢迎转载,转载请注明原文地址:http://blog.csdn.net/majianfei1023/article/details/45001021


一.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!


总结:

推荐:Python 装饰器 学习笔记

【装饰器原理】     装饰器本身是一个函数,她以其它函数作为传入自身的参数,经过处理后再返回一个新的函数。 【装饰器目的】     动态地修改一个函数的功能,

无参数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上面,用以修改类的属性

<span style="font-size:14px;">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()</span>

输出:

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的操作可以简化这一操作:

<span style="font-size:14px;">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)</span>

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


3.使用decorator来增加参数:

在上一篇lambda的用法 http://blog.csdn.net/majianfei1023/article/details/45269343 中讲解了用lambda来增加参数的例子。

def addBtnClickHander(btn,func):
	# 这个是通用接口,不可修改
	
	def hander():
		widget = "%s_name"%btn  # 根据btn取得
		func(widget)
	
	return hander

def main():
	btn = 'btn'
	name = 'name'
	addBtnClickHander(btn,lambda widget:onClick(widget,name))
	
def onClick(widget,name):
	print "onClick:",widget,name

我们这里用decorator来解决

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

def main():
	btn = 'btn'
	
	callback = request_decorator(onClick,'a')
	addBtnClickHander(btn,callback)

def onClick(name,widget):
	print "onClick:",widget,name

为什么可以这样?

addBtnClickHandler里面的func其实就是callback,func(widget)就是requestId_callback(*args),widget就是*args,那么我们要做的就是把name传进去,

return func(name,*args),就是调用了onClick(name,widget),明白了吧。


也许你会说,做出来了之后解释,大家都可以解释通,那么做之前怎么想到这么decorator可以满足要求呢,那么我们反着来,怎么设计decorator能增加参数,我们要改变的就是addBtnClickHandler里面的func,现在它只有一个参数,而我们需要两个(或者多个),那么就需要外界传进来,那么怎么传进来能满足要求呢

name = 'a'
func = onClick
def callback(widget):
	return func(widget,name)

我们要的不就是这个嘛,func就是onClick,callback = addBtnClickHandler的func,但是我们addBtnClickHandler的参数是固定的,怎么传name进来呢,decorator,对,用它来包裹起来传进来,

def decorator(func,name):
	def callback(widget):
		return func(widget,name)
	return callback
就像这样。


推荐:Python进阶之装饰器@decorator

什么是装饰器 本文参照慕课网廖学峰老师的视频教程整理 demo下载地址 :https://github.com/adamin1990/pydecorator 定义了一个函数 想在运行时动态增加功能 又

欢迎转载,转载请注明原文地址:http://blog.csdn.net/majianfei1023/article/details/45001021 一.decorator基础。 最初接触python是在大学毕业进入某一游戏公司后,最开始觉得python不严谨,

相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。