Python方法的偏应用。
项目描述
概述
此库提供了一种机制,通过AST(抽象语法树)转换在绑定方法上使用偏应用。您可以将偏应用用作优化方法,或代码生成的替代方案(直接或通过AST抽象)。
具体方法介于“currying”和“cooking”之间。我们通过遍历AST手动解释Python代码,遵循标准的Python逻辑(逐字解释每个节点)。在遍历时可以执行的计算被保存供以后引用(并在可能的情况下丢弃)。当可能时,使用运行时的解释器执行计算。
使用方法
您可以在任何绑定方法上使用cook函数。
>>> from curry import cook >>> cooked = cook(some_bound_method)
示例
起点是某个类的实例
>>> class Example(object): ... def __init__(self, value): ... self.value = value ... ... def __call__(self, other): ... return self.value + other
然后我们可以对某个实例的绑定调用方法使用偏应用
>>> from curry import cook >>> example = Example(1) >>> cooked = cook(example.__call__) >>> cooked(1) 2
更多的示例包含在功能测试套件中;它们也作为文档,是学习何时以及如何使用偏应用的起点。
序列化
该库包括内置的pickle模块(及其C扩展对应的模块)的替代方案
>>> from curry import dumps, loads >>> p = dumps(some_object) >>> some_object = loads(p)
这些函数有趣之处在于它们的性能和空间属性。生成的“pickle”通常小50%,并且解序列化的速度可能快50%(或慢200%)。但有一个缺点:序列化操作本身大约慢20倍。
这种pickle实现充其量是实验性的,但在你解pickle的频率远高于pickle的情况下可能适用。例如,它可以用于频繁读取的对象数据存储。
请注意,生成的pickle文件可能会受到解释器版本之间不兼容性的影响(它使用< span class="docutils literal">marshal模块来序列化代码)。
线性注释
部分应用是一个完全合理的命题;理论上,它应该适用于任何方法。要真正从中受益,类必须以适当的方式编写。本节详细说明了技术考虑因素,这些因素将指导开发者正确实现。
对象状态
抽象语法树由对应于Python语法的节点组成。表达式节点代表将最终执行的计算。可能在部分应用期间评估的表达式节点被称为< cite>已解决。在树结构方面,只有包含已解决子节点的节点才能被解决。
当一个表达式节点被解决时,它被替换为一个自定义节点类< span class="docutils literal">Resolved,该类包含计算值(可以是任何Python对象)。作为一个技术细节,已解决节点直到它们被代码生成器转换器访问之前都不会分配符号名称;内部,它们使用它们的对象标识符进行引用。
作为部分应用期间评估的结果,我们通常会不得不实例化新的(有状态的)对象。最初,这些对象的状态是已知的。AST还可能包含对应于变量输入的名称。这些变量在部分应用时是未定义的。当一个未定义的变量接触到一个已知的对象时,该对象的状态也变为未定义。
>>> out = [] >>> out.append("Hello") >>> out.append(string) >>> "".join(out)
如果string是未定义的,则在部分应用后,此代码的逻辑简化如下
>>> out = ["Hello"] >>> out.append(string) >>> "".join(out)
在对象状态首次变为未定义时,记录列表的状态。这将在稍后详细描述。
状态无效化
如果对象接触到一个未定义的对象,则该对象的已知状态将无效化。这只能在方法调用时发生,但我们必须注意Python实现其语法的这种方式。所有操作都传递到特殊方法,这显然是方法调用。
>>> out = ["Hello"] >>> out += string
这会在列表对象上调用< span class="docutils literal">__radd__方法。由于string是未定义的,它使列表的已知状态无效。
作为一个优化,转换器了解许多内置方法是静态的(对象的状体在调用时永远不会改变)
>>> out = ["Hello"] >>> out.count(string)
静态的< span class="docutils literal">count方法仅计算给定字符串的出现次数。我们不需要使列表无效。
重要的是要注意,任何(传递性)引用未定义对象的任何对象都必须本身是未定义的。
恢复对象状态
在上面的示例中,可以通过使用列表构造函数并传递已知的元素来将可变列表对象视为通过使用列表构造函数恢复。这本质上是优化。在一般情况下,我们可以使用保存和恢复状态的pickle,但这非常低效。
相反,由于已解决对象的完整状态是已知的,我们可以使用< span class="docutils literal">__new__构造函数和设置实例字典来恢复状态。
内置原语实例(如 list 和 set)没有实例字典。它们的属性就是它们的值。为了最佳性能,我们对这些情况进行了特殊处理。其他情况如下:
从可变内置类派生出的类。我们通过调用内置类的 __new__ 构造函数(传递类本身)来实例化实例,然后使用内置类的 __init__ 方法设置状态,最后恢复实例字典。
从 object 派生出的类。我们可以通过调用 object 的 __new__ 构造函数(传递类本身)来实例化实例,然后恢复实例字典。
上述任一情况定义了 __slots__ 属性。我们不恢复实例字典,而是设置每个属性。
要保存对象的通用状态,我们递归生成逻辑来实例化和更新其子对象(包含在对象字典中)的状态。请注意,此逻辑本身与 Python pickle 等效,尽管它以可执行代码的形式展开。
动态评估
exec 语句和 eval 函数的行为不同。字符串参数分别解析为语句和表达式,并直接插入 AST 中。
应用包括动态(局部)变量赋值和表达式评估,这两者都是模板处理的核心。
函数调用
有四种不同的情况。要么函数是已知的,要么参数是已知的,要么两者都知道,要么两者都不知道。
如果函数及其参数都是已知的,我们将立即计算结果。
如果函数已知,但参数未知,我们将函数作为闭包包含在内,并对其执行部分应用。
待办事项
星号参数和双星号关键字参数。
能够将方法标记为“静态”,例如使用装饰器。
变更日志
0.2(发布于 2009 年 7 月 29 日)
功能
添加了序列化功能(实验性),既可以作为独立函数使用,也可以与烹饪机制集成以保存和恢复对象状态。
错误修复
确保只对名称进行赋值。
0.1.1(发布于 2009 年 6 月 2 日)
修复了文档格式。
删除了调试语句。
0.1(发布于 2009 年 6 月 2 日)
首次公开发布。
项目详情
curry-0.2.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 7ecf097dc7ee3ced3d25b059f6e28e811bd45eb75495e9b013e2bd8fa419357d |
|
MD5 | 64a2232ee160c8c39bf992c2bb159098 |
|
BLAKE2b-256 | e3b1654dd3c20b2be334f80f7fc9be008b3098a2f55cd26d71a40c24717c7f35 |