原文: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中。例如,完全使用对象系统依赖于classmethod和property装饰器:
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():# ....
发展你的事业
编写装饰器在开始时并不容易。它不能一飞冲天,需要足够的努力来学习,以及充分了解细微差别,因此,许多开发者永远都不愿意花心思去掌握它。当你成为了你的团队中那个写得一手好装饰器的人,并且编写了可以解决实际问题的装饰器,那么其他开发者将会使用它们。因为一旦编写它们这一项工作完成了,那么装饰器是很容易使用的。这可以大规模放大你所写的代码的积极影响。并且,它还有可能让你成为一个英雄哦。
领取专属 10元无门槛券
私享最新 技术干货