Python 装饰器使用非常地简单。任何会使用 Python 函数的人都可以学习使用装饰器:

Python
1
2
3

@somedecorator
def some_function():
    print("Check it out, I'm using decorators!")

但是如何写装饰器却是另外一回事,而且这并不容易。你必须明白如下这些内容:

  • 闭包
  • 如何将函数做为一阶参数
  • 变量参数
  • 参数解包
  • 甚至是一些 Python 加载源代码的细节

这些知识都需要花费大量的时间去理解掌握。如果你已经累积了许多其它需要学习的内容。这些内容还值得你去学习吗?

对于我来说,答案是肯定的。希望你的答案也是肯定的。那么,自己写装饰器的好处是什么呢?或者说,它们会让我在日常开发过程中哪些事变得容易?

分析、日志与手段

对于大型应用, 我们常常需要记录应用的状态,以及测量不同活动的数量。通过将这些特别的事件包装到函数或方法中,装饰器可以很轻松地满足这些需求,同时保证代码的可读性。

Python
1
2
3
4
5
6
7
8
9
10
11
12
13

from myapp.log import logger
def log_order_event(func):
    def wrapper(*args, **kwargs):
        logger.info("Ordering: %s", func.__name__)
        order = func(*args, **kwargs)
        logger.debug("Order result: %s", order.result)
        return order
    return wrapper
@log_order_event
def order_pizza(*toppings):
    # let's get some pizza!

这个方法也可以用来记数或者记录其它某些指标。

验证以及运行时检查

Python 是一种强类型语言,但是变量的类型却是动态变化的。虽然这会带来很多好处,但是同时这也意味着更容易引入 bug。对于静态语言,例如 Java, 这些 bug 在编译阶段就可以被发现。因而,你可能希望在对传入或返回的数据进行一些自定义的的检查。装饰器就可以让你非常容易地实现这个需求,并一次性将其应用到多个函数上。

想像一下:你有许多函数,每个函数返回一个字典类型,该字典包含一个“summary ”域。这个域的值不能超过 80 个字符的长度。如果违反这个要求,那就是一个错误。下面这个装饰器会在错误发生时抛出 ValueError 异常:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

def validate_summary(func):
    def wrapper(*args, **kwargs):
        data = func(*args, **kwargs)
        if len(data["summary"]) > 80:
            raise ValueError("Summary too long")
        return data
    return wrapper
@validate_summary
def fetch_customer_data():
    # ...
@validate_summary
def query_orders(criteria):
    # ...
@validate_summary
def create_invoice(params):
    # ...

创建框架

一旦你掌握了如何写装饰器,你就能够从其使用的简单的语法中获益颇丰,你可以为语言添加新的语义使其使用更加简单。接下来最棒的就是你可以自己扩展 Python 语法。

事实上,很多开源框架都是使用的这样的方式。 Web 应用框架 Flask 就是使用装饰器将不同 URL 路由到不同处理 HTTP 请求函数的:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# For a RESTful todo-list API.
@app.route("/tasks/", methods=["GET"])
def get_all_tasks():
    tasks = app.store.get_all_tasks()
    return make_response(json.dumps(tasks), 200)
@app.route("/tasks/", methods=["POST"])
def create_task():
    payload = request.get_json(force=True)
    task_id = app.store.create_task(
        summary = payload["summary"],
        description = payload["description"],
    )
    task_info = {"id": task_id}
    return make_response(json.dumps(task_info), 201)
@app.route("/tasks/<int:task_id>/")
def task_details(task_id):
    task_info = app.store.task_details(task_id)
    if task_info is None:
        return make_response("", 404)
    return json.dumps(task_info)

这里有一个全局对象 app,此对象有一个 route 方法。此 route 函数返回一个用于修饰请求处理函数的装饰器。这背后的处理是非常复杂的,但是对于使用 Flask 的程序员来说,所有复杂的东西都被隐藏起来了。

在平时使用 Python 过程中,我们也会这样使用装饰器。例如,所有的对象都依赖于类方法与属性装饰器:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13

class WeatherSimulation:
    def __init__(self, **params):
         self.params = params
    @classmethod
    def for_winter(cls, **other_params):
        params = {'month': 'Jan', 'temp': '0'}
        params.update(other_params)
        return cls(**params)
    @property
    def progress(self):
        return self.completed_iterations() / self.total_iterations()

这个类有三个不同的 def 语句,但是每一个的语义都是不同的:

  • 构造器是一个简单的方法
  • for_winter 是一个类方法
  • progress 是一个只读的动态属性

@classmethod 装饰器与  @property 装饰器可以让我们在平时使用过程中非常方便地扩展 Python 对象的语义。

复用不能复用的代码

Python 提供了非常强大的工具以将代码包装成易复用的形式,这些工具包括:函数、函数式编程的支持以及一切皆对象的思想。然而,还是存在某些代码并不能通过使用这些工具进行复用。

假设有一个古怪的 API。你可以通过 HTTP 发送 JSON 格式的请求,它 99.9% 的情况下都是正确工作的。但是,小部分请求会返回服务器内部错误的结果。这时候,你需要重新发送请求。在这种情况下,你需要实现重试逻辑,像这样:

Python
1
2
3
4
5
6
7
8

resp = None
while True:
    resp = make_api_call()
    if resp.status_code == 500 and tries < MAX_TRIES:
        tries += 1
        continue
    break
process_response(resp)

现在假设你的代码库中有很都地方都进行调用了函数  make_api_call,那么是不是需要在每个调用的地方都实现这个 loop 循环呢?是不是每次添加一次调用都要实现一遍这个循环呢?这种模式能难有一个样板代码,除非你使用装饰器,那么这就变得非常简单了:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# The decorated function returns a Response object,
# which has a status_code attribute. 200 means
# success; 500 indicates a server-side error.
def retry(func):
    def retried_func(*args, **kwargs):
        MAX_TRIES = 3
        tries = 0
        while True:
            resp = func(*args, **kwargs)
            if resp.status_code == 500 and tries < MAX_TRIES:
                tries += 1
                continue
            break
        return resp
    return retried_func
This gives you an easy-to-use @retry decorator:
@retry
def make_api_call():
    # ....

让你的事业腾飞

刚开始写装饰器时可能不是那么容易。虽然这并不像造火箭那么难,但你也需要花费一些时间去学习,掌握其中的奥秘。大部分程序都能够掌握。当你成为团队里面能把装饰器写得很好并且能解决真正的问题的人时,此时其它开发者都会使用你开发的这些装饰器。因为一旦最难的部分,也就是实现装饰器完成后,使用装饰器是非常容易的。这可以极大的放大你所写代码的正面影响,这会让你成为团队的英雄。

我已经培训了成百上千的软件工程师让他们更高效地使用 Python,这些团队一致的反映装饰器是这其中最有价值也是他们在 Python 高阶编程中使用的最重要的工具。这也是为什么其成为了接下来的 Python 课程重点:在 2016 年 5月 25日与 26 日基础在线课程之后。

不管你是以何种方式学习实现装饰器,你都会因其能完成的工作而感到兴奋。毫无疑问,它能永久地改变你使用 Python 的方式 。

如何理解Python装饰器?相关推荐

  1. python装饰器原理-深刻理解python装饰器

    我们要完全理解python装饰器,不是很容易,主要归结有如下困难: 1. 关于函数"变量"(或"变量"函数)的理解 2. 关于高阶函数的理解 3. 关于嵌套函数 ...

  2. python装饰器-理解Python装饰器(Decorator)

    理解Python装饰器(Decorator) Python装饰器看起来类似Java中的注解,然鹅和注解并不相同,不过同样能够实现面向切面编程. 想要理解Python中的装饰器,不得不先理解闭包(clo ...

  3. python lock_python lock一步步教你理解Python装饰器

    请仔细看我们的decorator实例.我们定义了一个接受单个参数some_func的名为outer的函数.在outer内部我们定义了一个名为inner的嵌套函数.inner函数打印一个字符串然后调用s ...

  4. python装饰器由浅入深_由浅入深理解Python装饰器

    前提知识: 1.Python里函数也是一种对象: def shout(word="yes"): return word.capitalize()+"!" pri ...

  5. python装饰器理解_如何理解Python装饰器?

    首先,本垃圾文档工程师又来了.开始日常的水文写作.起因是看到这个问题如何理解Python装饰器?,正好不久前给人讲过这些,本垃圾于是又开始新的一轮辣鸡文章写作行为了. 预备知识 首先要理解装饰器,首先 ...

  6. 如何理解python装饰器

    如何理解python装饰器 @(Python学习-随手记)[Decorator, 帮助] 装饰器简介 装饰器decorator是一种高级python语法,可以对函数.方法.类进行加工. 装饰器常用场景 ...

  7. python装饰器-如何理解Python装饰器?

    我从以下几点,由浅入深详细讲解一下Python装饰器:什么事装饰器? 为什么用装饰器? 在哪里用装饰器? 然后以示例+讲解相结合的方式阐述,同时会讲解一些在很多教程和书籍中不会涉及到的内容. 什么是P ...

  8. python装饰器原理-深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

  9. 什么是python装饰器_深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

最新文章

  1. python sleep准确吗_Time.sleep对于Python计数器不准确?
  2. poj 1990 MooFest(转化成树状数组求和)
  3. 面试(后台开发,转载)
  4. 项目实践精解:C#核心技术应用开发
  5. NAS服务器局域网内IPad、手机、电视盒子等联网播放
  6. 对C# 程序员来说现在是到目前为止最好的时代
  7. python缩进tab还是空格_Python码农有话说:写代码时应该缩进使用 tab还是空格?...
  8. 工作317:uni-修改其他页面整体样式
  9. mysql存储过程中怎么睡几秒_MySql的逻辑架构
  10. 流畅的Python 2. 数据结构 - 序列构成的数组
  11. es分词查询与模糊查询
  12. IBC 2019 五篇文章阅读笔记
  13. 如何利用FME转换空间坐标系
  14. mysql临时数据库_MySQL临时目录 - mysql数据库栏目 - 自学php
  15. #9733;宣传广告变成社会标准
  16. Android studio 之 Menu(菜单)
  17. 常用软件自动安装,软件批量安装包升级版
  18. 中点画线法c语言程序,计算机图形学 :中点画圆法
  19. vue 点击打开新窗口
  20. checkbox选中触发事件

热门文章

  1. 爬虫基本功之学点JS(一)
  2. python海龟交易策略_Python的海龟交易法
  3. 一加手机史上超大购机优惠 以旧换新至高补贴3800元
  4. 花51万请明星和网红带货仅卖5000元,法院这样判了...
  5. 网络短视频内容审核趋严!短视频不得未经授权剪辑影视剧
  6. 贾跃亭:FF未来主义者们最终所要创造的社会价值 一定会得到资本市场的认可...
  7. 3K档真香旗舰!一加9RT今日正式开售:榨干索尼IMX 766旗舰传感器
  8. 华为P50标准版规格曝光:或搭载骁龙888 4G
  9. 中国联通董事李福申辞任
  10. 苹果股价周一下跌4.17% 市值今年首次跌破2万亿美元