关闭

Python装饰器----类型转换

标签: pythonpython装饰器可调用对象任务类
197人阅读 评论(0) 收藏 举报
分类:

类型转换

装饰器的一个更高级的使用情景,一个独特的很有价值的用法是,它装饰一个函数但返回一个类。在有大量的样板代码增加情况下,或者允许开发者在简单情况使用一个简单的函数,在复杂情况下才去子类化在一个应用中的API的类。
装饰器在这方面的一个应用的例子是在Python生态系统的中的一个流行的任务队列:celery。celery包提供了一个@celery.task装饰器,用来装饰一个函数。这个装饰器实际做的是返回celery内部类Task的子类,让被装饰函数在子类的run方法中被使用。
考虑下面的类似方式的简单例子:

class Task(object):
  """
    一个简单的任务类. Task类有一个run方法,用来启动任务 
  """
  def run(self, *args, **kwargs):
    raise NotImplementedError('Subclasses must implement   
    `run`.')
  def identify(self):
    return 'I am a task.'
def task(decorated):
  """
    返回一个类,如果这个类的run被调用,就会运行给定函数
  """
  class TaskSubclass(Task):
    def run(self, *args, **kwargs):
      return decorated(*args, **kwargs)
  return TaskSubclass

发生了什么? 装饰器创建了Task的子类并返回这个子类。这个子类是可调用的, 调用一个类创建那个类的实例并运行它的_init_方法。
这么做的价值是,它可以为很多扩展功能提供一个钩子。
基类Task可以定义很多方法,远不止是run.例如一个start方法可能会异步运行任务。基类也可能提供保存任务状态信息的方法。使用换出( swaps out)一个函数为一个类的装饰器可以允许开发者只考虑他的任务的实际功能部分,装饰器来做其余工作。

你可以使用这个类的实例,运行一下它的identify方法,实际看一下:

>>> @task
>>> def foo():
>>> return 2 + 2
>>>
>>> f = foo()
>>> f.run()
4>
>> f.identify()
'I am a task.'

一个坑(pitfall)
这个恰当的方法也携带来一些问题。This exact approach carries with it some problems. 尤其是,一旦一个任务函数被@task_class装饰,它就变成了一个类。考虑下面的以这种方式装饰的简单函数:

@task
def foo():
  return 2 + 2

现在,试着在解释器直接运行它:

>>> foo()
<__main__.TaskSubclass object at 0x10c3612d0>

这很糟糕!如果开发者运行它,装饰器以这样的方式修改了函数,它没有像人们期望的那样做。期待一个函数被声明为foo,然后绕着弯的使用foo().run()运行(目前的情况需要这样),实际上这难以接受。
满足这个需求应该放些心思在装饰器和Task类是如何构造的上面。看下面被修改的版本:

class Task(object):
  """
    一个简单的任务类. Task类有一个run方法,用来启动任务 
  """
  def __call__(self, *args, **kwargs):
    return self.run(*args, **kwargs)
  def run(self, *args, **kwargs):
    raise NotImplementedError('Subclasses must implement 'run`.')
  def identify(self):
    return 'I am a task.'
def task(decorated):
  """
   返回一个类,如果这个类的run被调用,就会运行给定函数
  """
  class TaskSubclass(Task):
    def run(self, *args, **kwargs):
    return decorated(*args, **kwargs)
  return TaskSubclass()

这里存在几处主要的不同. 首先是Task基类中的__call__. 第二处不同是@task_class装饰器返回了TaskSubclass类的实例而不是类本身。这是可接受的,因为对装饰器的唯一需求是它返回一个可调用对象,而Task中额外添加的call方法意味着它的实例现在是可调用对象。
为什么这种模式很有价值?再次强调,Task类很简单,但很容易发现更多功能如何能够被添加,这些功能对管理和运行任务很有用。

无论如何,这种方式保持了原始函数精神面貌,如果它被直接请求。
再次看下被装饰函数:

@task
def foo():
  return 2 + 2

现在,在解释器中运行:

>>> foo()
4 

这才是你想要的, 在背后装饰器返回了TaskSubclass实例 。
当这个实例在解释器中被调用,它的 __call__ method 被请求,
这会调用 run, 这会调用原始函数。
你可以看懂啊你仍然能获取你的实例,使用identify方法

>>> foo.identify()
'I am a task.'

现在你拥有一个实例,当直接调用时,就像在调用原始函数。然而,它能包括为其它功能提供的方法和属性。
这非常强大。它允许开发者写一个函数,这个函数容易,明确地移植到一个类,这个类提供了可选的方式给这个函数来被请求或者其它功能。

总结

装饰器是非常有价值的工具,你可以用它写出好管理,可读性强的Python代码。装饰器的价值在于这样的事实,它很明显而且可重用。它提供了出色的方式来使用样板代码,写一次,然后在多个地方使用。
是因为Python的数据模型提供了作为第一类对象(first-class objects)的函数和类,它能够像这个语言中任何其它对象一样被传递,被增强。

另一方面,这个模型也有缺点,尤其是,装饰器语法,在干净易于阅读的同时,也隐蔽了一个事实:一个函数被包装在另一个函数中,这带来了调试上的挑战。 写的差的装饰器可能制造错误,由于忽略了它包装的可调用对象的本质。(例如,忽视了绑定方法未绑定函数的区别).
除此以外,要牢记,像任何函数一样,解释器必须实际地在装饰器中运行代码,这有性能上的影响。对于这装饰器是不会不受影响的;
要多加注意你正在让你的装饰器做什么,也包括任何你写的其它代码。
考虑使用装饰器吧,当你采用前导(leading)和跟踪(trailing)功能,然后让它包装没有关联的函数。同样地,对于函数注册,信号通知,类增强的某种情况和许多其它事情,装饰器是非常有用的工具。

0
0
查看评论

Python实战小程序——装饰器

第四题:简述对Python装饰器的理解,写一个简单的装饰器。 要理解装饰器,我们先介绍一下几点python的基础知识。 1、作用域(命名空间)及变量生存周期 有过一点编程基础的都知道namespace分为: local namespace:作用于为当前函数 global namespace...
  • misayaaaaa
  • misayaaaaa
  • 2016-11-04 16:15
  • 837

Python装饰器详解

在上一篇文章中我们提到了闭包,也就是将函数作为返回值返回。闭包搞懂了之后,接下来的内容就很简单了。在定义了许多函数之后,我们希望扩展这些函数的功能,譬如在函数调用前后自动打印日志,但如果是一些通用的功能,修改每一个函数又会显得比较麻烦。最好的方法就是定义一个装饰器,给每个函数增加功能。这种在代码运行...
  • destinyuan
  • destinyuan
  • 2016-05-31 20:12
  • 1136

python 常用装饰器

@property 对于类的方法, Python内置的@property装饰器就是负责把一个方法变成属性调用的
  • flyDeDog
  • flyDeDog
  • 2017-03-31 15:17
  • 348

Python装饰器:简单装饰,带参数装饰与类装饰器

Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖、多练习是好的学习方式。 第一步:最简单的函数,准备附加额外功能 # -*- coding:gbk -*- '''示例1: 最简单的函数,表示调用了两次''...
  • dreamcoding
  • dreamcoding
  • 2013-02-25 22:58
  • 25777

Python-自定义装饰器

什么是装饰器?装饰器本质是一个函数,它可以在不改变原来的函数的基础上额外的增加一些功能。如常见的@classmethod,@staticmethod等都是装饰器,接下来记录下如何自定义个装饰器:刚刚说过了,装饰器的本质就是一个函数,所有想要自定义一个装饰器,首先自定义一个函数def decorate...
  • y472360651
  • y472360651
  • 2017-06-10 20:18
  • 1006

浅谈Python装饰器

浅谈Python装饰器 By 马冬亮(凝霜  Loki) 一个人的战争(http://blog.csdn.net/MDL13412) 前置知识 一级对象 Python将一切视为 objec t的子类,即一切都是对象,因此函数可以像变量一样被指向和传递,我们来看下面的例子: d...
  • MDL13412
  • MDL13412
  • 2014-03-30 22:07
  • 40664

Python 装饰器装饰类中的方法

title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] category: ['Python'] --- ...
  • hesi9555
  • hesi9555
  • 2017-04-18 09:54
  • 2256

Python多个装饰器的顺序

原文链接:http://www.cnblogs.com/nisen/p/6193426.html?utm_source=itdadao&utm_medium=referral 装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行...
  • jyhhhhhhh
  • jyhhhhhhh
  • 2017-01-20 02:51
  • 2067

python @装饰器 详解

距离我上一次写文章到现在已经颇有一段时间了,我想差不多也该在博客里开始新的系列了。 本文是我称为「这不是魔法」系列的第一篇,我准备在里面展示一些热门开源包提供的友好API是如何通过它们各自语言的原始语法构造的。 本文我们先来说说Flask,深入探讨Flask如何实现在函数上方写“@app...
  • uselym
  • uselym
  • 2016-09-12 13:59
  • 2617

Python 多层装饰器

前言Python 的装饰器能够在不破坏函数原本结构的基础上,对函数的功能进行补充。当我们需要对一个函数补充不同的功能,可能需要用到多层的装饰器。在我的使用过程中,遇到了两种装饰器层叠的情况,这里把这两种情况写下来,作为踩坑记录。情况1def A(funC): def decorated_C(...
  • u010185894
  • u010185894
  • 2017-05-05 10:26
  • 280
    个人资料
    • 访问:9555次
    • 积分:359
    • 等级:
    • 排名:千里之外
    • 原创:13篇
    • 转载:5篇
    • 译文:14篇
    • 评论:3条