python @装饰器 详解

本文通过逐步解析,展示了 Flask 框架中 @app.route 装饰器的实现原理,包括装饰器的基础概念及其如何绑定 URL 到视图函数。

距离我上一次写文章到现在已经颇有一段时间了,我想差不多也该在博客里开始新的系列了。

本文是我称为「这不是魔法」系列的第一篇,我准备在里面展示一些热门开源包提供的友好API是如何通过它们各自语言的原始语法构造的。

本文我们先来说说Flask,深入探讨Flask如何实现在函数上方写“@app.route()”就能在因特网上输出函数的执行结果。

下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的。

@app.route和其它装饰器

要想明白“@app.route()”的工作原理,我们首先需要看一看Python中的装饰器(就是以“@”开头的那玩意,下面接着函数定义)。

究竟什么是装饰器?没啥特别的。装饰器只是一种接受函数(就是那个你用“@”符号装饰的函数)的函数,并返回一个新的函数。

当你装饰一个函数,意味着你告诉Python调用的是那个由你的装饰器返回的新函数,而不仅仅是直接返回原函数体的执行结果。

还不是很明白?这里是一个简单的例子:

运行上述代码会输出以下结果:

Entering Function
Hello World
Exited Function

很好!

现在我们有点明白怎样创建我们自己的“@app.route()”装饰器了,但你可能会注意到有一个不同点,就是我们的simple_decorator不可以接受任何参数, 但“@app.route()”却可以。

那么我们怎样才能给我们的装饰器传参数?要实现这个我们只需创建一个“decorator_factory”函数,我们调用这个函数,返回适用于我们函数的装饰器。现在看看如果实现它。

给我们的输出是:

Start
Hello World
End
请注意在我们写@decorator_factory(“Start”, “End”)时,我们实际调用的是decorator_factory函数,实际返回的装饰器已经被用上了,代码很整洁,对吧?

把“app”放进“app.route”

现在我们掌握了装饰器怎样工作的全部前置知识 ,可以重新实现Flask API的这个部分了,那么把我们的目光转移到“app”在我们Flask应用中的重要地位上面来。

在开始解释Flask对象里面发生了什么之前,我们先创建我们自己的Python类NotFlask。

这不是个很有趣的类,不过有一样值得注意,就是这个类的方法也可以被用作装饰器,所以让我们把这个类写得更有趣一点,加一个称作 route的方法,它是一个简单的装饰器工厂。

这个装饰器和我们之前创建的那些最大的不同,在于我们不想修改被我们装饰的函数的行为,我们只是想获得它的引用。

所以,最后一步是我们打算去利用一个特性,就是用装饰器函数的副产品去保存一个提供给我们的路径之间的链接,装饰器函数应该与它关联起来。

为了实现这个,我们给我们的NotFlask对象加一个“routes”字典,当我们的“decorator”函数被调用,路径将被插入新字典中函数对应的位置。

现在我们就要完成了!可如果没法访问内部的视图函数,保存路径的字典又有什么用?让我们加入一个方法serve(path),当给定的路径存在时运行一个函数并给们我结果,当路径尚未注册时则抛出一个异常。

在这个系列我们只关注重现那些热门库提供的友好API,所以钩挂“serve”方法实现一个HTTP服务器其实有一点超出本文的范围,当然结果是确定的,运行下述片段:

我们会看到:

Hello World!

我们已经完成了一个的Flask网页上第一个例子的非常简单的重现,让我们写一些快速测试检测我们简单重现的Flask的“@app.route()”是否正确。

吸口气。

完全正确!所以,仅仅是一个简单的包含一个字典的装饰器, 就重现了Flask的“app.route()”装饰器的基本的行为。

在本系列的下一篇,也是Flask的app.route()的最后一篇,将通过解析下面这个例子来解释动态URL模式是如何工作。

待续!

Python 的 `@property` 装饰器是内置的属性管理装饰器,它将类方法转换为类属性访问接口,是 `property()` 内置函数的应用形式之一,与 `@staticmethod`、`@classmethod` 同属 Python 内置的函数装饰器 [^1][^3]。 ### 主要作用 - **创建只读属性**:通过 `@property` 装饰器修饰方法,可将方法转换为只读属性,像访问普通属性一样访问该方法,且不能对其进行赋值操作。例如: ```python class Human(object): def __init__(self, value): self._age = value @property def age(self): return self._age if __name__ == '__main__': peter = Human(18) print("the age of peter is {}".format(peter.age)) # peter.age = 20 # 如果要修改属性将会报错 ``` 在这个例子中,`age` 方法被 `@property` 装饰后,可像属性一样访问 `peter.age`,但尝试修改 `peter.age` 会报错 [^2]。 ### 运行机制 - **Getter 方法**:使用 `@property` 装饰的方法作为 Getter 方法,用于获取属性的值。例如: ```python class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius ``` 这里的 `radius` 方法就是 Getter 方法,可通过 `circle.radius` 来获取圆的半径 [^3]。 - **Setter 方法**:使用 `@属性名.setter` 装饰的方法作为 Setter 方法,用于设置属性的值。例如: ```python class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value < 0: raise ValueError("半径不能为负数") self._radius = value ``` 在这个例子中,`@radius.setter` 装饰的 `radius` 方法就是 Setter 方法,当对 `circle.radius` 进行赋值操作时,会调用该方法进行赋值,同时会进行输入验证,确保半径不为负数 [^3]。 - **Deleter 方法**:使用 `@属性名.deleter` 装饰的方法用于定义删除属性的行为。例如: ```python class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value < 0: raise ValueError("半径不能为负数") self._radius = value @radius.deleter def radius(self): del self._radius ``` 当执行 `del circle.radius` 时,会调用 `@radius.deleter` 装饰的方法删除半径属性 [^3]。 ### 核心价值 - **封装性**:隐藏属性操作的具体实现,外部只能通过定义好的接口来访问和修改属性,提高了代码的安全性和可维护性。 - **可维护性**:在不改变外部接口的前提下修改内部逻辑,例如在 Setter 方法中添加验证逻辑,不会影响外部代码对属性的访问方式。 - **安全性**:通过验证机制保护数据完整性,如在 Setter 方法中对输入值进行验证,确保数据符合要求 [^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值