Python 装饰器使用很是地简单。任何会使用 Python 函数的人均可以学习使用装饰器:python
Python程序员
@somedecorator def some_function(): print("Check it out, I'm using decorators!")
闭包可是如何写装饰器倒是另一回事,并且这并不容易。你必须明白以下这些内容:编程
如何将函数作为一阶参数json
变量参数api
这些知识都须要花费大量的时间去理解掌握。若是你已经累积了许多其它须要学习的内容。这些内容还值得你去学习吗?服务器
对于我来讲,答案是确定的。但愿你的答案也是确定的。那么,本身写装饰器的好处是什么呢?或者说,它们会让我在平常开发过程当中哪些事变得容易?闭包
分析、日志与手段app
对于大型应用, 咱们经常须要记录应用的状态,以及测量不一样活动的数量。经过将这些特别的事件包装到函数或方法中,装饰器能够很轻松地知足这些需求,同时保证代码的可读性。框架
Pythonide
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
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
# 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)
在平时使用 Python 过程当中,咱们也会这样使用装饰器。例如,全部的对象都依赖于类方法与属性装饰器:这里有一个全局对象 app,此对象有一个 route 方法。此 route 函数返回一个用于修饰请求处理函数的装饰器。这背后的处理是很是复杂的,可是对于使用 Flask 的程序员来讲,全部复杂的东西都被隐藏起来了。
Python
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 语句,可是每个的语义都是不一样的:
@classmethod 装饰器与 @property 装饰器可让咱们在平时使用过程当中很是方便地扩展 Python 对象的语义。
复用不能复用的代码
Python 提供了很是强大的工具以将代码包装成易复用的形式,这些工具包括:函数、函数式编程的支持以及一切皆对象的思想。然而,仍是存在某些代码并不能经过使用这些工具进行复用。
假设有一个古怪的 API。你能够经过 HTTP 发送 JSON 格式的请求,它 99.9% 的状况下都是正确工做的。可是,小部分请求会返回服务器内部错误的结果。这时候,你须要从新发送请求。在这种状况下,你须要实现重试逻辑,像这样:
Python
resp = None while True: resp = make_api_call() if resp.status_code == 500 and tries < MAX_TRIES: tries += 1 continue break process_response(resp)
Python如今假设你的代码库中有很都地方都进行调用了函数 make_api_call,那么是否是须要在每一个调用的地方都实现这个 loop 循环呢?是否是每次添加一次调用都要实现一遍这个循环呢?这种模式能难有一个样板代码,除非你使用装饰器,那么这就变得很是简单了:
# 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 的方式 。