python decorator心得体会

python decorator心得体会

  • 前言
  • 用途
    1. 给方法添加新的功能
    2. 给类增加或者删除方法
    3. 参数化的decorator
    4. 更改方法的默认调用行为
    5. 2和3的整合
      • 其实1和4可以归为一类特性,都是对现有方法的增强。**

前言

此小短文来源于qq群一位朋友的问题,问题如下:

下面这段代码的功能是什么?
def log(func):
    def wrapper(*args, **kw):
        print ‘call %s():‘ % func.__name__
        return func(*args, **kw)
    return wrapper

经过仔细分析,这个log函数接受func作为参数,然后返回一个内部函数wrapper,同时如果外部使用者继续使用返回的wrapper继续调用的话,实际上会通过print打印日志,然后调用最先传递的参数func。

一个简单的测试例子如下

def addAB(a, b):
    return a+b
print log(addAB)(1,21) 

执行输出结果如下:
call addAB():
22

对于上面的例子,拆解成两个部分分析。第一部分:使用log(addAB),它将addAB作为参数传递到log中,也就是说func=addAB,然后会返回内部的wrapper函数。第二部分: log(addAB)(1,21)被转换为wrapper(1,21),wrapper使用的可变参数形式,可以接受任意参数,wrapper函数首先打印print,然后调用func(*args, **kw),也就是相当于调用addAB(1,21),这也就可以说明print log(addAB)(1,21)的打印结果输出。

通过log的包装,对函数调用首先打印日志,然后再回到原始的函数调用。通过这个很简洁的函数,不需要修改func的代码,就可以对func函数实现调用日志的功能。后来,问这个问题的朋友告诉我了一种更简洁的方案,不需要显式的使用log,例子如下

@log
def addAB(a, b):
    return a+b
print addAB(1,21)

执行结果输出与上面相同

这种奇特的语法立刻燃起了我的兴趣,@log到底执行了什么?于是开始查看相关文档,找了一些文档,知道这种语法叫做decorator,常用在staticmethod和classmethod中。

python文档如下:

用途

给方法添加新的功能

这个前面已经介绍过了,请参见给方法添加新的功能。

前面的例子通过decorator的包装,可以给函数的调用添加日志。当然你可以针对自己的需求,增加自己想要的功能。在python cookbook中有通过 decorator 添加了诸如跟踪、日志记录、存储/缓存、线程锁定以及输出重定向之类的功能(本人未看过),但是根据上面的内容,应该可以看出是可以做到的吧。

例子1:输出增加html格式输出

def h1(func):
    def wrapper(*args, **kw):
        return ‘<h1>‘+func(*args, **kw)+‘</h1>‘
    return wrapper
def li(func):
    def wrapper(*args, **kw):
        return ‘<li>‘+func(*args, **kw)+‘</li>‘
    return wrapper    

@h1
@li
def foo():
    return ‘hello world‘
print foo() 

执行输出如下
<h1><li>hello world</li></h1>

可以看出经过两个decorator方法,输出结果确实增加了html标准语法格式。

例子2:用作缓存操作

cacheDic={}
def cache(func):
    def wrapper(*args, **kw):
        #print ‘args=‘, args, ‘ kw=‘,kw
        if args in cacheDic:
            return cacheDic[args]
        else:
            cacheDic[args] = func(*args, **kw)
            return cacheDic[args]
    return wrapper

#@cache
def fib(n):
    if n < 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

import timeit
s = ‘[fib(x) for x in range(20)]‘
s_cache=‘[cache(fib)(x) for x in range(20)]‘
print timeit.timeit(stmt=s, setup=‘from __main__ import fib‘, number=100)
print timeit.timeit(stmt=s_cache, setup=‘from __main__ import fib, cache‘, number=100)    

输出结果如下:
0.570032835007
0.00699996948242

可以看出使用了cache之后,时间减少了100倍。这个是使用的timeit的统计方法。由于如果使用了@cache的方式,自动给fib函数增加了cache功能。为了表示对比,这里使用了显式的cache方案,使用了cache(fib)(x)这样的调用方式,它等价于使用@cache。

给类增加,删除,修改方法

  • 角度1:模块加载时修改

    通过改写类的__new__方法,可给类增加方法,删除方法,修改方法。基本方向是覆盖__new__方法,修改__new__方法的class参数,例如给class参数赋值新的方法,即可以给类增加新的method,或者del掉class的某一个方法,即可以删除类的方法,或者直接将类的某一个方法用的新的方法代替,即可以修改方法。不过需要注意的是,这些修改都是在new方法调用的时候完成的,即创建类对象的时候完成的,并不需要创建任何实例变量,或者说实在模块加载期间完成。

  • 角度2:运行期修改

    可以通过decorator装饰器,在创建完实例对象之后调用方法的时刻,使其调用另外的实现代码,同样可以实现修改方法的目的。

具体请看下面的一个例子:

def minus(self, x, y):
    return x-y;
def multi(self, x, y):
    return x*y;
def div(self, x, y):
    return x*1.0/y;
def sayHello(self, *args, **kw):
    print ‘hello‘, self.__class__.__name__

def runSayHelloDecorator(func):
    def wrapper(self, *args, **kw):
        func(*args, **kw)
        print ‘in runtime, i have teached you to say hello runtime‘
    return wrapper

def change_pc_method(func):
    print ‘change‘
    if func.__name__ != ‘__new__‘:
        return func #do not change other name method

    #here goes other add or delete or change method
    def wrapper(cls, *args, **kw):
        #add
        cls.minus = minus
        cls.multi = multi
        cls.div   = div
        #change
        cls.sayHello = sayHello
        #delete
        if hasattr(cls, ‘to_delete‘):
            del cls.to_delete
        return super(cls.__class__, cls).__new__(cls, *args, **kw)
    return wrapper
class PC(object):
    def add(self, x, y):
        return x+y;
    @change_pc_method
    def __new__():
        pass
    def to_delete():
        print ‘this method to be deleted‘
    def sayHello():
        print ‘i can not say Hello, you can help me?‘
    @runSayHelloDecorator
    def runSayHello():
        print ‘in runtime, i can not say hello‘

pc = PC()
print pc.add(3,2),
print pc.minus(3,2),
print pc.div(3,2)
pc.sayHello()
pc.runSayHello()
try:
    pc.to_delete()
except Exception, e:
    print e
pc2=PC()    

程序输出结果如下:
change
5 1 1.5
hello PC
in runtime, i can not say hello
in runtime, i have teached you to say hello runtime
‘PC‘ object has no attribute ‘to_delete‘

上面的例子修改通过change_pc_method这种decorator覆盖了类的new方法,在其中对cls类对象增加了minus,multi,div三个方法,同时将其默认的sayHello的实现进行了修改。还有,通过运行期修饰函数调用的方式修改了runSayHello的实现。

注意:以上有一个小小的缺陷,在覆盖new方法修改sayHello中,将原有的sayHello给抛弃了。暂时并没有想到什么好的方案来做,很多时候原有的实现通常是有用的,这需要一些改进。如果您知道,请告知我。

参数化的decorator

参数化为运行时的决策提供了栖息地。本人并没有深刻理解这一思想的妙处,仅摆放在这里,便于后记。

#arg_wrapper, func_wrapper, wrapper
#以后命名统一如此命名:arg, func,wrapper
def arg_wrapper(word):
    def func_wrapper(func):
        def wrapper(self, *args, **kw):
            print self, word
            return func( *args, **kw) #func本身已经携带self参数
        return wrapper
    return func_wrapper

def FooMake(word):
    class Foo(object):
        @arg_wrapper(word)
        def say():
            pass
    return Foo()

foo1 = FooMake(‘this‘)
foo2 = FooMake(‘that‘)
foo1.say()
foo2.say()

程序输出结果:
<decorator_parameter.Foo object at 0x6ffffe60bd0> this
<decorator_parameter.Foo object at 0x6ffffe74c50> that

可以看出:say方法经过word参数化后具有不同的行为。不过本人还未知晓其用途

更改方法的默认调用行为

使用最基本的函数功能改变操作,通过装饰器扩展已有函数的功能。

def func_square(func):
def wrapper(arg):
    if hasattr(arg, ‘__getitem__‘):
        return map(func, arg);
    return func(arg)
return wrapper

@func_square
def square(x):
    return x*x;
print square(10)
print square([i for i in range(10)])

程序输出结果:
100
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

原始的square方法只能对单个元素求平方,通过扩展square的功能,还可以对列表的所有元素求平方。

2和3的整合

可爱的python一文中,提出了2和3的整合的一些引子。出于练习的角度,想写一个自动化一些的工具,奈何没有激情了,先就这样吧。

文章核心思想借用来源:可爱的 Python: Decorator 简化元编程

时间: 2024-11-23 03:29:49

python decorator心得体会的相关文章

Python初学心得体会

初学程序编程对于一个没学过程序的小白来说是一个很大的挑战! 首先分别搭建在Linux和Windows下的python环境,这里有一个小的麻烦,就是在Windows的浏览器使用jupyter时,需要在Linux中python家目录中将防火墙服务打开,否则无法打开网页(service iptables stop ,因为是临时关闭,所以每回使用都需要去关闭防火墙.作为一个新手多麻烦点还是比较好的,尤其是非计算机方面的!) python的基础语法,像for , while , if 语句,转义序列,标识

AngularJS心得体会

AngularJS早些时候有过了解,知道这是一个JS的MVC框架,同类型的框架还有Backbone等.这次是由于项目需要,学习了两天的Angular后开始着手改之前的项目代码,这里大概说一下这一周学习Angular的心得体会吧. 首相,使用Angular最大的感受就是它的设计思路完全不同于Jquery,jquery更倾向于对Dom的操作:而使用Angular则需要你有一个全局的认识,你必须知道你想要做成什么样子才可以下手去做,所以我感觉ng对前端开发的要求比jquery要高一些.先来看看Angu

关于软件项目管理的心得体会之一

目的 软件项目管理是一项涉及面较广,但是非常必要的一项技能.相较于软件开发中的其他专业技能, 又更加依赖于实践和阅历.这里想跟各位同仁分享一下自己在过往项目中的心得体会,结合些许耳熟能详的理论,起到抛砖引玉的作用. 局限性 项目管理既然是一门实践科学,所以这里跟大家分享之前,还是要说明局限性.因为我之前是在一家提供软件服务的传统软件公司工作, 所以很多项目的经验都来源于作为乙方的外包项目,同时,大部分项目都是移动相关领域.目前我在一家国内的互联网公司,从事的电商相关的应用项目. 开篇 想跟大家分

编程方法心得体会

编程学习心得和方法(转载,供新手参考)   即便是高级编程人员,像那些为开发软件工作的类库和组件高手,他们也不敢说自己精通每一种语言. 事实就是这样,每一种语言有着它们相似而又不同的语法,它们有着不同的关键字,发展出了各种各样的类库,它们的函数或者方法的声明定义都有不同,更多的差异不为我们所知. 每一种语言,它都有着太多的东西需要我们去理解和记忆,我们没有时间和精力做得太多. 对于初学者,我们最不应该急于求成,更不要试图一次性学习所有语言.事实上,我们可以选取某种语言学习.每一种语言都有着自己的

读《构建之法》的心得体会

读<构建之法>的心得体会 软件工程涉及的范围很广,对于即将投身IT业的学生而言,软件工程的内容又非常重要.读构建之法,尽管本书介绍了不少IT业正在使用的理论和技术,但是,这本书的主要思想并不是介绍所有的新思想和新技术,而是从这些新思想.新技术中总结出对自己在未来的工作中有用的东西. 在整本书中,印象最让我深刻的是“两个人的合作”这一章节.现代的软件产业经过几十年的发展,软件的结构随着用户需求的不断增加,软件的功能不断朝多元化与复杂化发展.不管是两个人的合作还是团队的合作,谈到合作不免提及规范这

Git的基本使用方法和安装&amp;心得体会

1. git的安装和github的注册.代码托管.创建organization.邀请member. (1)git的安装 因为我电脑是windows系统,所以下载的是git for windows.在官网下载非常卡,直接百度搜找百度那个下载就可以.下载后选择路径一直next就行了. (2)github的注册 没什么说的,虽然界面是英文,不过要是连这种程度的都看不懂你还是洗洗睡吧.按要求填完邮箱账号密码等常规东西就注册完了. (3)创建organization the organization's

IT增值服务实践心得体会:企业客户的钱比个人客户好赚得多

友情提示 本人喜欢直言不讳,不喜欢拐弯抹角.喜欢从客观和主观.自身和他人等多种角度去探讨问题.如有不当之处,欢迎吐槽. 若干心得体会1.企业客户的钱更好赚,个人客户的钱很难.  为什么这么说呢? a.企业有钱赚,为了更好地服务自己的客户,赚到更多的钱,花点钱,心甘情愿.只要你的商品和服务,不至于太坑. b.企业的功利心比较强,因此,非常有目标性,有动力,一定要解决自己遇到的问题. c.个人的付费意识很差,因为他们总认为自己赚的钱少,同时下意识地认为,别人花时间精力提供的服务或者劳动成果,不值钱.

关于代码的一些心得体会(大神勿喷)

关于代码的一些心得体会   前  言 Lms 入行也有很久了,一直都只是忙着工作学习,却一直没能好好静下心来好好整理一下自己.时间久了,慢慢的代码越来越熟悉,敲起来也越来越顺手,自己缺总感觉有些不对.我总觉得代码不应该就是这么简单,不应该像写记叙文一样,一条一条慢慢的就罗列出来了,返回去看了看自己刚写代码的时候功能也都能够实现了.但是还是有那么多可以优化的地方.我觉得好的代码不应该只是把功能实现那么简单,我觉得好的代码应该有以下几条特点:第一,命名要规范,第二,可复用性,第三,就是注释.当然,当

第8章 让开发板发出声音,蜂鸣器驱动 心得体会

第8章让开发板发出声音,蜂鸣器驱动 心得体会 通过学习Android深度探索(卷1)HAL与驱动开发的第8章 让开发板发出声音,蜂鸣器驱动,我加深对驱动的认识.以下主要是我对本节实验和参考在Linux驱动开发中使用PWM控制蜂鸣器中的一些见解. 先编写一个简单的蜂鸣器驱动程序,根据开发板上的硬件IO接口和Android驱动规范,在kernel中的driver目录中新建一个Buzzer文件,新建一个buzzer.c的源文 在目录下创建一个Makefile文件 先运行make clean命令清楚到编