原文

  • 基础知识
  • 回到 Decorators
  • Decorator
  • 为带参数的函数进行装饰
  • 通用 decorator
  • Chaining Decorators
  • 带有参数的 decorator
  • 几个跟 Class 相关的常用 Decorator
    • classmethod 和 staticmethod
    • property

装饰器 属于 metaprogramming 的一种,是在编译时一段程序尝试去修改另外一段程序的部分内容。

基础知识

在 python 中,everything 都是 objects。
Names 只是 tags bound to these objects。
甚至 classes,functions 都是 objects。
例如:

a = math.sin,则a就用了同sin一样的功能;
然后math.sin甚至可以指向其他函数,例如math.sin = func,则math.sin就变成了 func 函数的功能
而此时a仍然具有sin函数的功能

函数可以作为参数传递给其他函数,例如map, filter, reduce.
这些接受函数作为参数的函数被称为 higher order functions.

函数也可以作为返回值被返回,例如在上一节中提到的 Closure。

回到 Decorators

functions 和 methods 都被称为 callable,因为他们都可以被执行。
实际上,任何 object,只要实现了__call__()方法,都是一个 callable
所以,Decorator 就是一个返回 callable 的 callable。
Decorator 使用函数作为参数,增加一些功能做成一个新的函数,然后返回它。

def make_pretty(func):def inner():print("I got decorated")func()return innerdef ordinary():print("I am ordinary")>>> ordinary()
I am ordinary>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary

在上面的例子中,make_pretty()就是一个Decorator。
Decorator 就像一个 wrapper 一样。
Decorator 就好像把传入的函数 “decorate/装饰” 了一番,然后又吐了出来。

@Decorator

python 中提供了一个更简单的使用 decorator 的方法,就是@符号:

@make_pretty
def ordinary():print("I am ordinary")

就等同于

def ordinary():print("I am ordinary")ordinary = make_pretty(ordinary)

注意,下面的例子中,重新赋值后的 ordinary 已经不是 def 定义的ordinary 了,而是一个经过 decorator 修饰的 ordinary。
因此上面的例子中,@符号直接创造了经过修饰的 ordinary,省去了中间的形式。

为带参数的函数进行“装饰”

例子

def divide(a, b):return a/b

上面的函数显然有缺陷,在 b == 0 时会 raise ZeroDivisionError,因此我们可以对之进行“装饰”:

def smart_divide(func):def inner(a,b):print("I am going to divide",a,"and",b)if b == 0:print("Whoops! cannot divide")returnreturn func(a,b)return inner@smart_divide
def divide(a,b):return a/b

经过修饰后,divide在当 b == 0 时会警告,变成了一个没有返回值的函数(返回 None);在b != 0 时返回正常的结果,又是一个有返回值的函数。
当使用者调用 divide(n / m)时,实际上是在调用一个内部函数 inner(n / m)
而该内部函数使用了原始的、未被“修饰”的函数,即作为参数传入的 func,即写在@smart_divide下面的那个形式。

通用 decorator

你会发现,实际上inner() 函数使用了和原始的被修饰函数同样的参数。根据这个原理,我们可以写出更通用的格式:

def works_for_all(func):def inner(*args, **kwargs):print("I can decorate any function")return func(*args, **kwargs)return inner

那么这个 decorator 可以“修饰”任何函数。

Chaining Decorators

被“修饰”的函数可以再次被“修饰”,就成了 decorator 嵌套:

def star(func):def inner(*args, **kwargs):print("*" * 30)func(*args, **kwargs)print("*" * 30)return innerdef percent(func):def inner(*args, **kwargs):print("%" * 30)func(*args, **kwargs)print("%" * 30)return inner@star
@percent
def printer(msg):print(msg)
printer("Hello")

输出结果是:

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

注意,@star 和 @percent 的前后顺序很重要!不然就是不一样的结果。

带有参数的 decorator

有时候你会看到下面的形式:

@decorator(param)
def func():pass

不要怕,根据其原理,那么就相当于:

def func():passfunc = decorator(param)(func)

可见,decorator 本身就是一个返回 Decorator 的 Decorator 函数。其形式诸如:

def decorator(argument):def real_decorator(function):def wrapper(*args, **kwargs):funny_stuff()something_with_argument(argument)function(*args, **kwargs)more_funny_stuff()return wrapperreturn real_decorator

几个跟 Class 相关的常用 Decorator

1. @classmethod 和 @staticmethod

  • 在类中,normal method 也称为 instance method,定义 instance method 时,需要默认给定一个 self 参数,代表了实例本身。
class Date(object):def __init__(self, day=0, month=0, year=0):self.day = dayself.month = monthself.year = year>>> date1 = Date(6, 11, 2017)
  • 在类中,还有一类 method 称为 class method,定义 class method 时,需要默认给定一个 cls 参数,代表了类本身。
...@classmethoddef from_string(cls, date_as_string):day, month, year = map(int, date_as_string.split('-'))date1 = cls(day, month, year)return date1date2 = Date.from_string('11-09-2012')

因为在 python 中没有 重载/Overloading,所以不能用 date2 = Date('11-09-2012') 的形式定义另外一个构造函数,因此需要类提供新的方法来进行“构造”。
在上面的例子中,@classmethod 为该类引入了一个新方法 from_string
@classmethod 修饰符是必须的,否则,如果没有这个修饰符,即使你为 from_string 写了 cls 作为一参,它实际上仍然是意义上的 self,并且你只能通过 = Date().from_string(... 的形式使用它。

  • 在类中,还有一类 method 是 static method,它对应于 C++ 中的 static method,其实就是一个普通的函数,只不过被包含在了类中,此时”类”的作用仅仅是作为一个 naming space.
...@staticmethoddef is_date_valid(date_as_string):day, month, year = map(int, date_as_string.split('-'))return day <= 31 and month <= 12 and year <= 3999# usage:
is_date = Date.is_date_valid('11-09-2012')

同样,该方法不需要任何类的实例就可以直接使用。
当然,对于 class method 和 static method,你通过类的实例使用也是没问题的,即 is_date = Date().is_date_valid('11-09-2012') 也合法,但没人这样用,因为它使得 class methodstatic method 的含义变得模糊了。

2. @property

见下一节

深入浅出 Python Decorators相关推荐

  1. 回归素材(part10)--深入浅出python机器学习

    学习笔记,仅供参考,有错必纠 文章目录 深入浅出python机器学习 线性回归 基本原理 使用 L 2 L_2 L<

  2. python getattr_深入浅出Python模块

    引言 上周发表了<老胡讲Python>系列第一篇,得到了很多的关注.在第一篇中,大家表示对模块不是很明白,而模块的概念在Python中是十分重要的,于是今天带来<老胡讲Python& ...

  3. 《深入浅出Python量化交易实战》:散户也能学会的数字化交易策略

    前言 您可能不知道,许多专业的交易机构已经采用设定程序完成自动化交易,通过机器语言,解密盘面的走势,从而实现持续盈利的目的. (文末送读者福利) 这并非什么秘密,他们正是借助了这样的数字化工具进行操作 ...

  4. 《深入浅出Python》与《Python网络数据采集》读后感

    本学期在课程之外,自主计划学习Python语言以及爬虫知识.书籍方面,采用了O`Reilly的<深入浅出Python>与<Python网络数据采集>.前者用于Python语法的 ...

  5. 深入浅出python系列(三):逻辑判断语句

    深入浅出python系列:   深入浅出python系列(一):基本数据类型   深入浅出python系列(二):运算符 [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权): 本博客的 ...

  6. 学习《深入浅出python量化交易交易实战》第一章(笔记)

    1.学习<深入浅出python量化交易交易实战>第一章 记录学习过程中的代码和一些坑 1.1 基础(名词解释) 1.1.1 CAPM (Capital Asset Pricing Moda ...

  7. Python应用实战案例-深入浅出Python随机森林预测实战(附源码)

    前言 组合算法也叫集成学习,在金融行业或非图像识别领域,效果有时甚至比深度学习还要好.能够理解基本原理并将代码用于实际的业务案例是本文的目标,本文将详细介绍如何利用Python实现集成学习中随机森林这 ...

  8. 深入浅出 python epub_《机器学习从认知到实践(第2辑)(套装共3册,Python+TensorFlow)》epub+mobi+azw3...

    套装书目  · · · · · · <Python机器学习实践指南> <TensorFlow机器学习项目实战> <TensorFlow技术解析与实战> 分册简介   ...

  9. 原理+代码|深入浅出Python随机森林预测实战

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 寒梅最堪恨,长作去年花. 前言 组 ...

  10. 机器学习 python_送书 | 深入浅出Python机器学习

    大家好,我是村长,  为了感谢您一直以来的关注与支持,给大家送了点福利! 以后每周三都会送免费送技术书!您有哪方面技术书籍的需求,也可以微信告诉我~~本周送的书籍如下,后台回复 送书 参与 书籍简介 ...

最新文章

  1. 面试题--特别是字节对齐
  2. 石川es6课程---9、面向对象-基础
  3. 5、Power Map—实例:填充地图
  4. java泛型的作用和实现原理_java泛型的作用及实现原理
  5. redis6持久化主从复制
  6. sql server 2008安装需要一直重启。但重启后又没有达到效果。
  7. C++ list,STL list
  8. 赚钱真的要抓住风口,抓住风口猪都能飞
  9. php 删除 session 文件,如何删除php中的session文件
  10. xv6 System Call
  11. cad插件加载bplot成功用不了_Batchplot批量打印命令无效,对照情况进行解决
  12. android百度经纬度转gps坐标,GPS经纬度怎么转百度经纬度
  13. 单路电压表c语言编程,基于ADC0809和AT89S52单片机的数字电压表的设计
  14. Tether市值十月下跌超过10亿美元
  15. Linux系统的基本使用指南(速成,帮助快速上手使用Linux系统)
  16. 计算广告(3)----搜索广告召回匹配
  17. 想自由查看自己网站每个页面流量情况?这个工具可以满足需求!
  18. 【分享-免费OCR工具 在线+移动端】免费在线图片多国文字识别
  19. 生于80年代需读的80本书
  20. Mysql优化——唯一索引和普通索引的选择

热门文章

  1. 传智播客-刘意-java深入浅出精华版学习笔记Day06
  2. Asp.net自定义控件开发任我行(1)-笑傲江湖
  3. Spring揭秘——什么是IOC和DI
  4. Spring 揭秘之Spring框架的由来
  5. java;break语句
  6. python遗传算法工具箱的使用_Python遗传算法框架——Geatpy学习笔记(一)
  7. 数据平台SQL开发详解与函数使用
  8. 高校科研管理系统源代码_高校科研信息管理系统
  9. (论文阅读)图像超分辨率的回顾与展望
  10. ngrok转发mysql连接_使用小米球 -Ngrok 实现内网转发