Python Decorators II: Decorator Arguments

October 19, 2008

(本文是(Python3之模式和用法)一书的章节节选第二部分,点击阅读第一部分)

回顾:不含参数的decorators

在前文中,我介绍了如何使用不含参数的decorators,并使用类来实现。因为我发现这样做更容易接受。

如果创建了一个无参decorator,被decorated的函数被传至构造器,每次调用decorated函数时就会调用__call__()方法:

class decoratorWithoutArguments(object):

def __init__(self, f):

"""

If there are no decorator arguments, the function

to be decorated is passed to the constructor.

"""

print "Inside __init__()"

self.f = f

def __call__(self, *args):

"""

The __call__ method is not called until the

decorated function is called.

"""

print "Inside __call__()"

self.f(*args)

print "After self.f(*args)"

@decoratorWithoutArguments

def sayHello(a1, a2, a3, a4):

print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"

sayHello("say", "hello", "argument", "list")

print "After first sayHello() call"

sayHello("a", "different", "set of", "arguments")

print "After second sayHello() call"

decorated函数的所有参数会被传至__call__()。输出结果是:

Inside __init__()

After decoration

Preparing to call sayHello()

Inside __call__()

sayHello arguments: say hello argument list

After self.f(*args)

After first sayHello() call

Inside __call__()

sayHello arguments: a different set of arguments

After self.f(*args)

After second sayHello() call

注意,__init__()是唯一一个被调用执行decoration的方法,每次调用decorated的sayHello()时就会调用__call__()。

含有参数的decorators

现在让我们来修改上面的代码,看看向decorator加入参数后结果是什么。

class decoratorWithArguments(object):

def __init__(self, arg1, arg2, arg3):

"""

If there are decorator arguments, the function

to be decorated is not passed to the constructor!

"""

print "Inside __init__()"

self.arg1 = arg1

self.arg2 = arg2

self.arg3 = arg3

def __call__(self, f):

"""

If there are decorator arguments, __call__() is only called

once, as part of the decoration process! You can only give

it a single argument, which is the function object.

"""

print "Inside __call__()"

def wrapped_f(*args):

print "Inside wrapped_f()"

print "Decorator arguments:", self.arg1, self.arg2, self.arg3

f(*args)

print "After f(*args)"

return wrapped_f

@decoratorWithArguments("hello", "world", 42)

def sayHello(a1, a2, a3, a4):

print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"

sayHello("say", "hello", "argument", "list")

print "after first sayHello() call"

sayHello("a", "different", "set of", "arguments")

print "after second sayHello() call"

从输出结果可以看到,加入参数使程序执行发生了很大变化。

Inside __init__()

Inside __call__()

After decoration

Preparing to call sayHello()

Inside wrapped_f()

Decorator arguments: hello world 42

sayHello arguments: say hello argument list

After f(*args)

after first sayHello() call

Inside wrapped_f()

Decorator arguments: hello world 42

sayHello arguments: a different set of arguments

After f(*args)

after second sayHello() call

现在decoration方法调用构造器,然后就马上调用__call__(),后者只能包含一个参数(函数对象)且返回替代原有函数的decorated函数对象。注意当前decoration期间__call__()仅被调用一次,此后从__call__()返回的decorated函数就可以在实际调用中使用了。

虽然这种机制有一定合理性—构造器在这里可获取decorator参数,但__call__()对象不能再作为decorated函数使用了。因此你必须使用__call__()执行decoration—可能第一次遇到这种与无参情况截然不同的方式你会比较吃惊,何况还必须编写和无参decorator完成不同的代码。

含decorator参数的decorator函数

最后,让我们看一个更复杂一点的decorator函数实现,它需要你处理所有细节:

def decoratorFunctionWithArguments(arg1, arg2, arg3):

def wrap(f):

print "Inside wrap()"

def wrapped_f(*args):

print "Inside wrapped_f()"

print "Decorator arguments:", arg1, arg2, arg3

f(*args)

print "After f(*args)"

return wrapped_f

return wrap

@decoratorFunctionWithArguments("hello", "world", 42)

def sayHello(a1, a2, a3, a4):

print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"

sayHello("say", "hello", "argument", "list")

print "after first sayHello() call"

sayHello("a", "different", "set of", "arguments")

print "after second sayHello() call"

输出结果为:

Inside wrap()

After decoration

Preparing to call sayHello()

Inside wrapped_f()

Decorator arguments: hello world 42

sayHello arguments: say hello argument list

After f(*args)

after first sayHello() call

Inside wrapped_f()

Decorator arguments: hello world 42

sayHello arguments: a different set of arguments

After f(*args)

after second sayHello() call

decorator函数的返回值必须是一个封装待decorated函数的函数。也就是说,Python会保存返回函数然后在decoration期间调用,并传递待decorated函数。这也是为何有三层函数的原因:里面那个函数才是被替换的。

由于闭包,wrapped_f()有权访问decorator参数arg1, arg2和arg3,而无需像在class版本中那样显式存储它们。然而,我也是在这里发现了“显胜于隐(explicit is better than implicit)”。即使该函数版本看起来要更加简洁紧凑,但我发现还是类版本容易理解,当然也就容易修改和维护。

下一节内容

在下一节中我会给出decorators的一些实例—基于Python开发的build system—然后在最后一节讨论类decorators。

decorators 参数_Python Decorators(二):Decorator参数相关推荐

  1. python中非可选参数_python基础教程函数参数

    python里有很多的内置函数给我们的工作带来了很多发便利,在我们实现某些功能或者优化代码的时候,可以自己定义一个函数,同时我们在定义一个类的时候也会用到函数的一些知识去构造一个方法,这里就涉及到一些 ...

  2. 虎书学习笔记4:图形学基础数学(隐式二维直线、隐式二次曲线、二维参数曲线、二维参数直线、二维参数圆)

    关于图形学的基础数学知识 基础数学 隐式二维直线 我们最熟悉的直线:斜截式 他的隐式方程为: 我们再函数y-mx-b=0两边同乘一个系数,将得到一摸一样的直线. 因为两个点觉得一条直线,所以必然满足: ...

  3. python中可变长度参数_Python的可变长参数

    一.*args和**kwargs的概念 1.可以接收所有的实参,就是万能参数,也叫可变长参数,比如: *args,**kwargs 2.*的使用定义 函数定义时: *代表聚合 合数调用时: *代表打散 ...

  4. python 打印执行命令的参数_python之获取命令行参数

    1. getopt模块: getopt模块用于抽出命令行选项和参数,也就是sys.argv.命令行选项使得程序的参数更加灵活.支持短选项模式和长选项模式. 1) 主要函数:getopt(args, s ...

  5. python 可变参数 关键字参数_Python关键字及可变参数*args,**kw原理解析

    可变参数 顾名思义,函数的可变参数是传入的参数可以变化的,1个,2个到任意个.当然可以将这些 参数封装成一个 list 或者 tuple 传入,但不够 pythonic.使用可变参数可以很好解决该问题 ...

  6. python自定义函数参数_python自定义函数的参数之四种表现形式

    (1)def a(x,y):print x,y 这是最常见的定义方式,调用该函数,a(1,2)则x取1,y取2,形参与实参相对应,如果a(1)或者a(1,2,3)则会报错 (2)def a(x,y=3 ...

  7. python魔法参数_python中的魔法参数:*args和**kwargs

    def foo(*args, **kwargs): print 'args = ', args print 'kwargs = ', kwargs print '------------------- ...

  8. python代码设置超参数_Python 机器学习:超参数调优

    1.什么是超参数 超参数(hyper parameters)就是机器学习或深度学习算法中需要预先设置的参数,这些参数不是通过训练数据学习到的参数:原始算法一般只给出超参数的取值范围和含义,根据不同的应 ...

  9. python二维列表做参数_python sum函数iterable参数为二维list,start参数为“[]”该如何理解...

    1楼讲的很对,我这边举例详细说明一下. 如下为help(sum)解释 Help on built-in function sum in module builtins: sum(iterable, s ...

最新文章

  1. EJB3.1 JBoss7.1 Eclipse3.7
  2. 【剑指offer-Java版】17合并两个排序链表
  3. Visual Studio registry capture utility 已停止工作的解决办法
  4. android HDMI 清晰度 分辨率
  5. OpenCV交互式相机校准应用
  6. HBase基本操作-java api
  7. php整数和浮点数比较,php 浮点数怎么进行比较?
  8. leetcode之String to Integer (atoi)
  9. 小程序源码:独立后台带分销功能月老办事处交友盲盒
  10. post和get的区别
  11. 有两个杯子,一个5升一个6升,很多水,请问如何取得3升水
  12. gcj编译java_用GCJ编译Java源文件成脱离JRE的exe可执行文件
  13. y2002音乐源码php,解析MP3等媒体文件的超强php类-getid3
  14. 【数据结构与算法】五、哈希表和链表
  15. Greenplum中内存设置不合理导致的报错
  16. Shell脚本(三)
  17. java 做日历_java编程实现日历
  18. 给网站开发者推荐18个在线手册,值得收藏
  19. 基于OpenCV实现海岸线变化检测
  20. Symbian上的流媒体视频实验(1)

热门文章

  1. vue 获取验证码倒计时组件
  2. SQL Server CLR 启用、部署
  3. web学习1--web项目的WEB-INF目录
  4. 分页查询插件PageHelper 5.x版本
  5. 信息抽取--新词提取
  6. Java弱引用(WeakReference)的理解与使用
  7. [NOIP2010提高组]关押罪犯
  8. iOS开发——高级技术本地化与国际化详解
  9. 如何配置一个最基本的web富文本编辑器?--之wangEditor(验证成功)
  10. 目录服务用户OSX: ARD的基于目录服务用户权限