你需要学习编写Python装饰器的五大理由

原文:5 reasons you need to learn to write Python decorators

Python装饰器很容易使用。任何知道如何写python函数的人都应该学习使用装饰器:

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

但是写装饰器的代码有点难,你需要知道下面这些东西:

闭包

如何将函数作为参数

可变参数

参数解包

Python如何加载其源代码的一些细节

这都要花费大量的时间去理解和掌握。有许多东西要学习。这值得吗?

对我而言,答案都是“一千次,YES!”。对你来说,也会是这样的。编写装饰器的主要好处是什么…… 在你日常的开发中,它们怎样让你轻松有力呢?

分析,记录和工具

尤其是对大型应用,我们往往需要专门衡量发生了什么事,记录量化不同活动的指标。通过在函数或方法中封装这些值得关注的事件,一个装饰器能够非常可读并且轻松地处理这种需求。

frommyapp.logimportlogger

deflog_order_event(func):defwrapper(*args, **kwargs): logger.info("Ordering: %s", func.__name__) order = func(*args, **kwargs) logger.debug("Order result: %s", order.result)returnorderreturnwrapper @log_order_eventdeforder_pizza(*toppings):# let's get some pizza!

相同的方法可以用于记录个数或其他指标。

验证和运行时检查

Python的类型系统是强类型,但极具动态。由于这种好处,也意味着一些bug会试图悄悄混进来,而那些更静态类型的语言(例如JAVA)会在编译时捕获这种bug。除此之外,你可能想要在数据的出口和入口执行更加复杂的自定义检测。装饰器可以让你轻松地处理这一切,并且可以立即将其应用到许多函数。

想象一下:你有一组函数,每个都返回一个字典,其(在其他字段)包含了一个名为“summary”的字段。该字段的值不能超过80个字符;如果违反了,则属于错误。下面是一个装饰器,它在该错误发生时抛出一个ValueError

defvalidate_summary(func):defwrapper(*args, **kwargs): data = func(*args, **kwargs)

iflen(data["summary"]) >80:

raiseValueError("Summary too long")

returndata

returnwrapper

@validate_summarydeffetch_customer_data():# ...@validate_summarydefquery_orders(criteria):# ...@validate_summarydefcreate_invoice(params):# ...

创建框架

一旦你掌握了如何编写装饰器,你将从这种简洁的语法中获益.

事实上,许多流行的开源框架都使用装饰器。web app框架Flask用它将URL路由到处理该HTTP请求的函数上:

# For a RESTful todo-list API.@app.route("/tasks/", methods=["GET"])defget_all_tasks(): tasks = app.store.get_all_tasks()

returnmake_response(json.dumps(tasks),200)

@app.route("/tasks/", methods=["POST"])defcreate_task(): payload = request.get_json(force=True) task_id = app.store.create_task( summary = payload["summary"], description = payload["description"], ) task_info = {"id": task_id}

returnmake_response(json.dumps(task_info),201)

@app.route("/tasks//")deftask_details(task_id): task_info = app.store.task_details(task_id)

iftask_infoisNone:

returnmake_response("",404)

returnjson.dumps(task_info)

上面的代码中有一个全局对象,称为app,以及一个名为route的方法,该方法接收某些参数。route方法返回一个装饰器,用于处理器函数。引擎之下发生的事情将非常错综复杂,但是从使用Flask的人的角度来说,所有这些复杂性都被隐藏了。

以这种方式使用装饰器也出现在stock Python中。例如,完全使用对象系统依赖于classmethodproperty装饰器:

classWeatherSimulation:def__init__(self, **params): self.params = params

@classmethoddeffor_winter(cls, **other_params): params = {'month':'Jan','temp':'0'} params.update(other_params)

returncls(**params)

@propertydefprogress(self):returnself.completed_iterations() / self.total_iterations()

这个类有三个不同的def声明。但是它们的语义完全不同:

构造函数是一个普通的方法

for_winter是一个类方法(classmethod),提供一种工厂,而

progress是一个只读的动态属性

@classmethod@property装饰器的简单性,使得其易于在日常使用中扩展Python对象语义。

重用不可能重用的代码

Python为你提供了一些非常强大的工具,具有表达功能的函数语法,函数式编程支持,以及一个全功能的对象系统,封装代码到易于重用的形式。然而,有一些代码复用模式不能单独封装。

想想使用一个古怪的API。你通过HTTP,使用JSON向对端发起请求,在99.9%的情况下它正常工作。但是……这些请求的一些部分会使得服务器返回一个内部错误。在这种情况下,你会实现一些重试逻辑,像这样:

resp =NonewhileTrue: resp = make_api_call()

ifresp.status_code ==500andtries

现在,想象一下,你有几十个类似于make_api_call()的函数,它们被整个代码库调用。你要到处实现那个while循环吗?你要在每次添加一个新的API调用函数时都来一遍?这种模式使得它难以拥有样板代码。除非你使用装饰器。然后,它很简单:

# The decorated function returns a Response object,# which has a status_code attribute. 200 means# success; 500 indicates a server-side error.defretry(func):defretried_func(*args, **kwargs): MAX_TRIES =3tries =whileTrue: resp = func(*args, **kwargs)

ifresp.status_code ==500andtries

returnretried_func

# This gives you an easy-to-use @retry decorator:@retrydefmake_api_call():# ....

发展你的事业

编写装饰器在开始时并不容易。它不能一飞冲天,需要足够的努力来学习,以及充分了解细微差别,因此,许多开发者永远都不愿意花心思去掌握它。当你成为了你的团队中那个写得一手好装饰器的人,并且编写了可以解决实际问题的装饰器,那么其他开发者将会使用它们。因为一旦编写它们这一项工作完成了,那么装饰器是很容易使用的。这可以大规模放大你所写的代码的积极影响。并且,它还有可能让你成为一个英雄哦。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180402A1JTSC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券