前言

前几天在看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. python笔记之调用系统命令

    python笔记之调用系统命令 目前我使用到的python中执行cmd的方式有三种 使用os.system("cmd") 该方法在调用完shell脚本后,返回一个16位的二进制数, ...

  2. Hopscotch(POJ 3050 DFS)

    Hopscotch Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2845   Accepted: 1995 Descrip ...

  3. 实用AutoHotkey功能展示

    AutoHotkey是什么 AutoHotkey是一个自动化脚本语言. AutoHotkey有什么用 可以让你用热键操控一切,操作电脑就像在表演魔术 我的口号 AutoHotkey!用过都说好! Au ...

  4. Java Spring MVC

    Spring MVC的实现包括 实现Controller类和基于注解的Controller RequstMapping方式 依赖: <!-- https://mvnrepository.com/ ...

  5. Typecho 代码阅读笔记(一) - 页面渲染及路由机制

    转载请注明出处:http://blog.csdn.net/jh_zzz 从 index.php 开始看, /** 初始化组件 */ Typecho_Widget:: widget('Widget_In ...

  6. 8051、ARM和DSP指令周期的测试与分析

    在实时嵌入式控制系统中,指令周期对系统的性能有至关重要的影响.介绍几种最常用的微控制器的工作机制,采用一段循环语句对这几种微控制器的指令周期进行测试,并进行分析比较.分析结论对系统控制器的选择有一定的 ...

  7. WPF笔记(1.4 布局)——Hello,WPF!

    原文:WPF笔记(1.4 布局)--Hello,WPF! 这一节只是第2章的引子.布局要使用Panel控件,有四种Panel,如下:DockPanel,就是设置停靠位置布局模型.StackPanel, ...

  8. perl 爬取csdn

    <pre name="code" class="python">use LWP::UserAgent; use POSIX; use HTML::T ...

  9. Lake Counting (POJ No.2386)

    有一个大小为N*M的园子,雨后积起了水,八连通的积水被认为是链接在一起的求出园子里一共有多少水洼? *** *W* *** /** *进行深度优先搜索,从第一个W开始,将八个方向可以到达的 W修改为 ...

  10. 转(havel 算法)

    http://www.cnblogs.com/wally/p/3281361.html poj 1659(havel算法) 题目链接:http://poj.org/problem?id=1659 思路 ...