python大神交流网站_2019全球Python交流会:全球大神探讨的问题,可能你我都曾遇到过...

近期Python全球交流会开展,下面一起来看看具体,现在Python上的一些问题吧,期待3.8可以解决这些问题。

a8773912b31bb0513fc14879a44439b04bede0ba.jpeg?token=e92cb74662b2d8471f4327f6bd38409b&s=BB3B7484C29169C64DBAB5490300D09B

Petr Viktorin:扩展模块和子解释器

当Python子解释器加载用C编写的扩展模块时,它往往会无意中与已加载相同模块的其他子解释器共享状态,除非非常仔细地编写该模块。Petr Viktorin在Python语言峰会上发表了详细描述问题的建议,并提出了一个更清晰的子解释器隔离。

基于Python的库使用子解释器进行隔离

Python可以在单个进程中运行多个解释器实例,使每个子解释器与其他解析器相对隔离。将来有两种方法可以使用此功能,但两者都需要改进Python。首先,Python可以通过为每个子解释器提供自己的全局解释器锁(GIL)并在它们之间传递消息来实现并行性。Eric Snow在PEP 554中提出了这种子解释器的使用。另一种情况是库碰巧使用Python作为其实现的一部分。例如,Viktorin描述了一个内部使用Python和NumPy的模拟库,或者使用Python和asyncio的聊天库。一个应用程序应该可以加载多个这样的库,每个库都使用Python解释器,没有交叉污染。这个用例是Viktorin演示的主题。他说,问题在于“CPython还没有为此做好准备”,因为它没有妥善管理全球状态。

有许多种全球状态

Viktorin描述了解释器中各种全局状态的层次结构,或者树。进程状态:例如,打开文件描述符。运行时状态:Python内存分配器的数据结构和GIL(直到PEP 554)。解释器状态: “builtins”模块的内容和所有导入模块的dict。线程状态:线程本地,如asyncio的当前事件循环; 幸运的是,这是每个翻译。上下文状态:隐含状态,如decimal.context。模块状态:在文件范围或使用“global”关键字声明的Python变量,实际上创建了模块本地状态。

模块状态变化的让人惊讶

通过一系列示例,Viktorin展示了模块级状态的微妙行为。首先是一个非令人惊讶的例子,通过重新导入它来重新创建纯Python模块的状态:

import enum

old_enum = enum

del sys.modules ['enum']

import enum

old_enum == enum #False

但令人惊讶的是,C扩展模块在重新导入时似乎只会重新创建:

import _sqlite3

old_sqlite3 = _sqlite3

del sys.modules ['_ sqlite3']

import _sqlite3

old_sqlite3 == _sqlite3 #False

最后一行似乎表明这两个模块是截然不同的,但正如Viktorin所说——“这是谎言。”模块的初始化不会重新运行,并且两个模块的内容是共享的:

old_sqlite3.Error is _sqlite3.Error # True

它太容易了使用这些共享内容污染其他子解释器 - 实际上,C扩展的模块状态因此是进程全局状态。

模块必须周到地重写

以新风格 编写的C扩展避免了子解释器的这个问题。并非所有标准库中的C扩展都已更新; Christian Heimes评论说,该ssl模块必须移植到新的初始化方式。虽然找到必须移植的模块很简单,但实际移植需要考虑。编码人员必须仔细区分不同类型的全球状态。C静态变量是进程全局变量,PyState_FindModule返回对模块的解释器 - 全局引用,PyModule_GetState返回模块本地状态。必须将每个模块数据块有意地放置在层次结构中的一个级别上。

作为一个多么棘手的例子,维克托林指出了一个错误

csv模块。如果导入两次,则异常处理中断:

import _csv

old_csv = _csv

del sys.modules ['_ csv']

import _csv

尝试:

#将无效数组传递给reader():应该是一个字符串,而不是1。

列表(old_csv.reader([1]))

除了old_csv.Error:

#exception子句应该捕获错误但不捕获。

通过该old_csv.reader函数应该引发一个old_csv.Error与该except子句匹配的实例。实际上,该csv模块有一个bug。重新导入时,它会覆盖解释器级别的状态,包括_csv.Error类型,而不是将其状态保持在模块本地级别。

交流会成员们一致认为这是一个错误,但Viktorin坚持认为这个特定的错误只是一个更大问题的症状:编写正确的隔离扩展模块太难了。Viktorin和三位共同作者提出了PEP 573来缓解这个问题,特别注意异常类型。Viktorin建议所有模块作者保持模块级别的状态。他意识到这并不总是可行的:例如,Python标准库readline

module包装readline库,它具有全局钩子。这些必然是过程全球状态。他问听众,这个场景应该如何处理?如果readline在多个子解释器中导入错误,应该会出错吗?他说,“有一些想法要做。”无论如何,CPython需要一个良好的默认值。

编写C扩展的正确方法是使用模块本地状态,这应该是从C存储状态的最明显的地方.Viktorin似乎最新的样式API确实强调了他想要的模块本地状态,但是它们还不为人所熟知。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值