前言

前几天在看Flask框架,对于非常神奇的@语法,不是非常的理解,回来补装饰器的功课。阅读很多的关于装饰器的文章,自己整理一下,适合自己的思路的方法和例子,与大家分享。

app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

1、装饰器是什么

装饰器是Python语言中的高级语法。主要的功能是对一个函数、方法、或者类进行加工,作用是为已经存在的对象添加额外的功能,提升代码的可读性。

装饰器是设计模式的一种,被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等

2、装饰器的语法

装饰器的语法如下:

当前Python的装饰器语法如下:
@dec2
@dec1
def func(arg1, arg2, ...):
....
return funx 上面的代码相当于: def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))

装饰器可以用def的形式来定义。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。

装饰器新建了一个可调用对象,也就是return 返回的函数funx,在新增的函数中,可以添加我们需要的功能,并通过调用原有函数来实现原有函数的功能。

3、装饰器的使用

3.1不带参数的装饰器

定义装饰器非常的简单:

def deco(func):
"""无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法"""
print "before myfunc() called."
func()
print "after myfunc() called."
return func @deco
def myfunc():
print " myfunc() called." myfunc()
myfunc()

定义好装饰器后,就可以通过@语法来使用了,在函数的定义前调用@+装饰器函数名,即可使用。上面这个装饰器在使用的时候有一个问题,即只在第一次被调用,并且原来的函数多执行一次。执行输出如下:

before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called. --函数多执行一次的输出
myfunc() called. --第二次调用,装饰器不生效

要保证新函数每次被调用,使用下面的方法来定义装饰器

def deco(func):
"""无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法"""
def _deco():
print "before myfunc() called."
func()
print "after myfunc() called."
#return func 不需要返回func
retrun _deco
@deco
def myfunc():
print " myfunc() called."
return 'OK' myfunc()
myfunc()

函数输出如下:

before myfunc() called.
myfunc() called.
after myfunc() called.
before myfunc() called.
myfunc() called.
after myfunc() called.

这样可以看到,装饰器每次都得到了调用。

3.2带参数的函数进行装饰器

def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print(" after myfunc() called. result: %s" % ret)
return ret
return _deco @deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b myfunc(1, 2)
myfunc(3, 4)

输出:

before myfunc() called.

myfunc() called.

After myfunc() called. result: 3

before myfunc() called.
myfunc() called.
After myfunc() called. result: 7

内嵌函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数。

3.3装饰器带参数

def decoWithArgs(arg):
"""由于有参数的decorator函数在调用时只会使用应用时的参数而不接收被装饰的函数做为参数,
所以必须返回一个decorator函数, 由它对被装饰的函数进行封装处理"""
def newDeco(func): #定义一个新的decorator函数
def replaceFunc(): #在decorator函数里面再定义一个内嵌函数,由它封装具体的操作
print "Enter decorator %s" %arg #进行额外操作
return func() #对被装饰函数进行调用
return replaceFunc
return newDeco #返回一个新的decorator函数 @decoWithArgs("demo")
def MyFunc(): #应用@decoWithArgs修饰的方法
print "Enter MyFunc" MyFunc() #调用被装饰的函数

输出:

nter decorator demo

Enter MyFunc

这个情形适用于原来的函数没有参数,新增加打印的情况。常见适用的地方是增加函数的打印日志。

3.4对参数数量不确定的函数进行装饰

下面的例子是一个邮件异步发送的例子,函数的参数数据部确定,装饰器实现了对于邮件发送函数的异步发送。

from threading import Thread

def async(f):
def wrapper(*args, **kwargs):
thr = Thread(target = f, args = args, kwargs = kwargs)
thr.start()
return wrapper @async
def send_async_email(msg):
mail.send(msg) def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender = sender, recipients = recipients)
msg.body = text_body
msg.html = html_body
send_async_email(msg)

并且这个装饰器可以适用一切需要异步处理的功能,做到非常好的代码复用。

3.5让装饰器带类参数

class locker:
def __init__(self):
print("locker.__init__() should be not called.") @staticmethod
def acquire():
print("locker.acquire() called.(这是静态方法)") @staticmethod
def release():
print(" locker.release() called.(不需要对象实例)") def deco(cls):
'''cls 必须实现acquire和release静态方法'''
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, cls))
cls.acquire()
try:
return func()
finally:
cls.release()
return __deco
return _deco @deco(locker)
def myfunc():
print(" myfunc() called.") myfunc()
myfunc()

输出为:

before myfunc called [__main__.locker].
locker.acquire() called.(this is staticmethon)
myfunc() called.
locker.release() called.(do't need object ) before myfunc called [__main__.locker].
locker.acquire() called.(this is staticmethon)
myfunc() called.
locker.release() called.(do't need object )

装饰器总结

当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现。

参考文章

感谢以下几位大神:

http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html

http://www.cnblogs.com/Jifangliang/archive/2008/07/22/1248313.html

http://www.cnblogs.com/vamei/archive/2013/02/16/2820212.html

Python 装饰器学习以及实际使用场景实践的更多相关文章

  1. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  2. (转载)Python装饰器学习

    转载出处:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方 ...

  3. Python装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...

  4. Python 装饰器学习心得

    最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客.这篇学习笔记主要是记录近来看的有关Python装饰器的东西. 0. 什么是装饰器? 本质上来说,装饰器其实就是一个特殊功能的函数 ...

  5. python 装饰器学习(decorator)

    最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...

  6. Python装饰器学习(九步入门)

    这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- coding:gbk -*- '''示 ...

  7. python装饰器学习详解-函数部分

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...

  8. python装饰器学习笔记

    定义:本质上就是个函数,(装饰器其他函数)就是为了给其他函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 #-*-coding:utf-8-*- 1 imp ...

  9. 【转】九步学习python装饰器

    本篇日志来自:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 纯转,只字未改.只是为了学习一下装饰器.其实现在也是没有太看明白 ...

随机推荐

  1. [Head First Python]6. 定制数据对象:打包代码与数据

    相同功能,演进实现 数据文件 sarah2.txt sarah Sweeney,2002-6-17,2:58,2.58,2:39,2-25,2-55,2:54,2.18,2:55,2:55 1- 返回 ...

  2. JAVA JNI学习

    JAVA JNI   JNI是Java Native Interface的缩写,中文为JAVA本地调用.从Java1.1开始,Java Native Interface(JNI)标准成为java平台的 ...

  3. js序列化json对象

    SerializeJsonToStr : function( oJson ) { if( oJson == null ) return "null"; if( typeof(oJs ...

  4. SqlServer sysobjects_table

    --这是查询所有表的信息 select * from sysobjects where xtype='U'; --这是查询表的数量 select count(*) from sysobjects wh ...

  5. Course Schedule 解答

    Question There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may ...

  6. live555 源代码简单分析1:主程序

    live555是使用十分广泛的开源流媒体服务器,之前也看过其他人写的live555的学习笔记,在这里自己简单总结下. live555源代码有以下几个明显的特点: 1.头文件是.hh后缀的,但没觉得和. ...

  7. Mac 下纯lua(三)

    文件处理 直接使用io调用 io.close();文件流关闭 io.flush():如果文件流以bufferd缓存模式处理,输入不会立即存入文件,需要调用本函数 io.input(file):输入 i ...

  8. javax.mail用smtp服务器发送带附件的邮件

    jar包: javax.mail-1.5.5.jar maven配置: <dependency> <groupId>com.sun.mail</groupId> & ...

  9. Android学习总结——Service组件

    从Service的启动方式上,可以将Service分为Started Service和Bound Service.在使用Service时,要想系统能够找到此自定义Service,无论哪种类型,都需要在 ...

  10. java学习笔记day06---匿名内部类

    1.匿名内部类:其实就是内部类的简化形式,它所体现的就是一个类或者接口的子类对象.前提:     内部类必须继承或实现外部类或接口. 格式:    new 父类&接口(){};    其实就是 ...