Python PEP 302 是什么?

hooopo · 2012年08月13日 · 最后由 bhuztez 回复于 2012年08月13日 · 10746 次阅读

这东西的上下文是什么?是因为什么东西翻到这个上面的。

我粗看感觉是:2.3 以前的 python import 机制太挫了,就实现了一个新的 import 机制。

这个问我就可以了。我都被 Django 整得快成 PEP 302 砖家了...

#3 楼 @bhuztez 就是来问你的呀.............

#4 楼 @hooopo 问题是你现在问得也太宽了吧,我从哪里说起都不知道

#5 楼 @bhuztez 我貌似看明白一点儿 说是可以从 zip 文件里 import

#6 楼 @hooopo 这坑太大,从 zip 文件里 import 只是其中一个很小的应用而已...

#7 楼 @bhuztez 讲讲其他的应用哇

#8 楼 @hooopo 这个问题比较乱,我思路也没理清楚。我就尽量讲一下我现在对这个问题的认识好了。

这个问题和操作系统有关系,和语言也有关系。或者你可以把一个语言的运行期看成一个操作系统。

首先来看 UNIX: Everything is a file。假如Everything is a file只是字面意思,那等于啥都没说。其实这里有两个问题,到哪里去找文件,怎么操作文件。

对于第一个问题,UNIX 的答案是一个树形的File System/Namespace。在这里不对这两个概念进行区分。

对于第二个问题,UNIX 的答案是统一的API/Interface/Protocol/Message。在这里不对这三个概念进行区分。

接着来看 Smalltalk: Everything is an object。类似的。同样有这两个问题。而对于第一个问题的答案,经典的 Smalltalk 80 并不比 UNIX 更好,Smalltalk 80 就直接让你在名字前面加前缀来区分,就相当于你需要把它脑补成树形的。

对于第一个问题同时也隐含着,一切都可以以文件的形式暴露在文件系统上。这也就是你可以看见/proc/sys这类的目录的原因。

这就是为什么 Plan 9 有file server,Hurd 有Translator。Linux 后来也引入了Fuse。也就是说,有了这个机制和对应的file server,你可以把一个 zip 文件,mount成一个目录,可以直接把可以通过 HTTP/FTP 访问到的远端文件mount成一个本地文件。

假如所有操作系统都原生有这样的机制的话,Python 就没有必要引入 PEP 302 的概念。PEP 302 就是定义了一组 API,让你可以把任何东西,无论它以什么形式存在或者不存在,以 Python object 的形式 import 到 Python 命名空间里,只要你实现了这组 API。

前几天在 Hacker News 上提到的Bitey ,无非就是为 LLVM bitcode 实现了这组 API 罢了。

但是,PEP-302 只是一个 work-around,只能为 Python 解决一小部分的问题。Python 进程运行的时候,事实上有两个独立的 namespace,一个是操作系统的,一个是 Python 的。这也导致了很多麻烦。按照 Python 惯例,并不会把操作系统的 namespace 映射到 Python 的 namespace 里,也不会把 Python 的 namespace 映射到操作系统的 namespace。很多程序,同时会需要一些静态文件,比如图片啥的。而 PEP-302 只定义了get_data,这其实是远远不够的。而且,即便所有 API 都定义好了,这仍然不够好,尽管这对目前的 Python 来说很可能是唯一的办法。这将导致你为同一件事写了两份代码。

我觉得要真正解决这个问题,得使用 Plan 9 那样的操作系统,把 Python 的 namespace 暴露成/python,能用 Python 实现file server,另外,Python 得实现像 Erlang 那样的抢占式调度,那还是 Python 么?我觉得可能还是直接在 Erlang 的基础上改比较靠谱,至少,Erlang 是抢占式调度的,并且也有真正的 namespace 实现 (尽管几乎所有人都用前缀)。

#9 楼 @hooopo 相关的一个问题。Ruby 里面 require 到底会干哪些事?Ruby 里 require 的语义和 Python 的 import 不太一样吧。

Python 里面一个模块就对应一个文件,import 之后,就相当于是一个寄生在 sys.modules 下的 dict,所以 importer 的实现可以很简单。

#11 楼 @bhuztez Ruby 里的require/load很简单,就是把文件加载到环境里。

require "file_path" 相当于下面的伪代码:

file_content = File.read("file_path")
eval(file_content)

#12 楼 @hooopo 这个还真和 Python 的 import 有点不一样啊。

__import__(xxx)

相当于

content = read("xxx.py")
module = new_module()
eval content at module
sys.modules['xxx'] = module

import xxx的时候,就是引用 sys.modules['xxx'] 没有就__import__

而在 Ruby 里,require两次,代码也是只执行一次的吧。所以,你上面那个是load的语义。require是没被require过会去load一下。而load必须得eval,因为直接访问的 namespace 对于所有文件来说是一样的。

#13 楼 @bhuztez Ruby 里的 namespace 是自己管理的,所以可能存在冲突,最近还看到这里有一个帖子就是说的 namespace 冲突的问题

#14 楼 @hooopo 我的意思就是说 Python 的 importer 可以跳过 eval,直接返回结果。module 可以随便建的。

比如像下面这样的伪码。

module = new_module()
module['a'] = 1
module['b'] = 2
sys.modules['xxx'] = module

Python 的 importer 机制对 Ruby 实现类似机制的参考意义并不大。

Perl 那个才和 Ruby 比较接近。

http://perldoc.perl.org/functions/require.html

但 Ruby 又不一样,Ruby 还可以reopen class啊。所以问题就来了,无论如何都要eval一次,importer 应该返回,源代码,还是 ast,还是 bytecode?但是无论返回哪一种,像bitey那样的功能该怎么实现?

需要 登录 后方可回复, 如果你还没有账号请 注册新账号