返回介绍

实现细节

发布于 2024-01-29 22:24:14 字数 1790 浏览 0 评论 0 收藏 0

装饰器的代码依赖于内省API和对参数传递的细微限制。为了完整地泛化,以便我们可以从原则上整体模拟Python的参数匹配逻辑,来看到哪个名称以何种模式传入,但是,这对于我们的工具来说太复杂了。如果我们能够根据所有期待的参数的名称集合来按照名称匹配传入的参数,从而判断哪个位置参数真正地出现在给定的调用中,那将会更好。

函数内省

已经证实了内省API可以在函数对象以及其拥有我们所需的工具的相关代码对象上实现。第19章简单介绍过这个API,我们在这里实际地使用它。期待的参数名集合只是附加给一个函数的代码对象的前N个变量名:

同样的API在较早的Python中也可以使用,但是,在Python 2.5及更早的版本中,func.__code__attribute拼写为func.func_code in 2.5(为了可移植性,新的__code__属性在Python 2.6中也冗余地可用)。在函数上和代码对象上运行一个dir调用,以了解更多细节。

参数假设

给定期待的参数名的这个集合,该解决方案依赖于Python对于参数传递顺序所施加的两条限制(在Python 2.6和Python 3.0中都仍然成立):

·在调用时,所有的位置参数出现在所有关键字参数之前。

·在def中,所有的非默认参数出现在所有的默认参数之前。

也就是说,在一个调用中,一个非关键字参数通常不会跟在一个关键字参数后面,并且在定义中,一个非默认参数不会跟在一个默认参数后面。在两种位置中,所有的"name=value"语法必须出现在任何简单的"name"之后。

为了简化,我们也可以假设一个调用一般是有效的——例如,所有的参数要么接收值(按照名称或位置),要么将有意忽略而选取默认值。不一定要进行这种假设,因为当包装逻辑测试有效性的时候,函数还没有真正调用——随后包装逻辑调用的时候,调用仍然可能失效,由于不正确的参数传递。只要这不会引发包装器在糟糕地失效,我们可以改进调用的验证。这是有帮助的,因为在调用发生之前验证它,将会要求我们完全模仿Python的参数匹配算法——再一次说明,这对我们的工具来说是一个过于复杂的过程。

匹配算法

现在,给定了这些限制和假设,我们可以用这一算法来考虑调用中的关键字以及忽略的默认参数。当拦截了一个调用,我们可以作如下假设:

·*pargs中的所有N个传递的位置参数,必须与从函数的代码对象获取的前N个期待的参数匹配。对于前面列出的每个Python的调用顺序规则都是如此,因为所有的位置参数在所有关键字参数之前。

·要获取按照位置实际传递的参数的名称,我们可以把所有其他参数的列表分片为长度为N的*pargs位置参数元组。

·前N个期待的参数之后的任何参数,要么是按照关键字传递,要么是调用时候忽略的默认参数。

·对于要验证的每个参数名,如果它在**kargs中,它是按照名称传递的;如果它在前N个期待的参数中,它是按照位置传递的(在这种情况下,它在期待的列表中的相对位置给出了它在*pargs中的相对位置);否则,我们可以假设它是在调用时候忽略的并且默认的参数,不需要检查。

换句话说,对于假设*pargs中前N个实际传递的位置参数,必须与期待的参数列表中的前N个参数匹配,并且任何其他的参数要么必须是按照关键字传送并位于**kargs中,要么必须是默认的参数,我们就可以略过对调用时忽略的参数的测试。在这种方法下,对于最右边的位置参数和最左边的关键字参数之间的、或者在关键字参数之间的、或者在所有的最右边的位置之后的那些忽略掉的参数,装饰器将省略对其检查。可以跟踪装饰器及其测试脚本,看看这是如何在代码中实现的。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文