跳至主要内容

Python 兼容性

本页面的目标是指出在 PyPy 和 CPython 上运行 Python 之间的一些差异。

TL;DR

纯 Python 代码可以运行,但对象生命周期管理方面存在一些差异。使用 CPython C API 的模块可能可以运行,但无法通过 JIT 实现加速。我们鼓励库作者使用 CFFIHPy 代替。

如果您正在寻找如何将 PyPy 与科学 Python 生态系统一起使用,我们建议您使用 conda,因为他们为 PyPy 打包了像 scikit-learn 和 SciPy 这样的常用库。

引用计数、__del__ 和资源使用

纯 Python 代码中不会被修复的主要区别是 PyPy 不支持引用计数语义,以在对象的 __del__ 被调用时“自动”释放状态。以下代码不会立即填充文件,而是在一段时间后,当 GC 进行收集并刷新输出时才会填充,因为只有在调用 __del__ 方法时才会关闭文件。

open("filename", "w").write("stuff")

正确的修复方法是

with open("filename", "w") as f:
    f.write("stuff")

同样的问题——没有关闭你的文件——也会出现在你的程序打开大量文件而没有显式关闭它们的情况下。在这种情况下,你很容易达到系统对同时打开的文件描述符数量的限制。

PyPy 可以使用命令行选项 -X track-resources 运行(例如,pypy -X track-resources myprogram.py)。当 GC 关闭未关闭的文件或套接字时,这会产生一个 ResourceWarning。还会给出分配文件或套接字的位置的回溯,这有助于找到缺少 close() 的位置。

类似地,请记住,您必须 close() 一个未耗尽的生成器,以便立即执行其待处理的 finallywith 子句。

def mygen():
    with foo:
        yield 42

for x in mygen():
    if x == 42:
        break    # foo.__exit__ is not run immediately!

# fixed version:
gen = mygen()
try:
    for x in gen:
        if x == 42:
            break
finally:
    gen.close()

更一般地说,__del__() 方法不像在 CPython 上那样具有预测性:它们在 PyPy 中“稍后运行”(或者如果程序在此期间完成运行,则根本不运行)。请参阅 此处了解更多详细信息

为什么内存使用率如此之高?

请注意,PyPy 仅在 madvise() 系统调用(至少在 Linux、OS X、BSD)或 Windows 上之后才会将未使用的内存返回给操作系统。重要的是要意识到您可能不会在 top 中看到这一点。未使用的页面用 MADV_FREE 标记,它告诉系统“如果你在某个时候需要更多内存,请获取此页面”。只要内存充足,top 中的 RES 列可能会保持较高。(此规则的例外情况是那些没有 MADV_FREE 的系统,我们在其中使用 MADV_DONTNEED,它强制降低 RES。这包括 Linux <= 4.4。)

更多信息

可以在 我们的开发者网站 上找到更完整的已知差异列表。