为Python包创建别名?

5 投票
3 回答
11691 浏览
提问于 2025-04-16 06:30

我有一个文件夹,叫做 Storage,里面放满了名字很长、很难记的包,比如 mypackage-xxyyzzww。当然,Storage 这个文件夹在我的 PYTHONPATH 里。因为这些包的名字太长了,我把它们都链接成了更简单的名字,比如 mypackage

现在,我不想依赖文件系统的符号链接来实现这个功能,所以我试着在 sys.pathsys.modules 上做了一些调整。目前我做的事情大概是这样的:

import imp
imp.load_package('mypackage', 'Storage/mypackage-xxyyzzww')

这样做到底有多糟糕?将来会不会出问题?有趣的是,文档里甚至没有提到 imp.load_package 这个函数。

补充一下:除了不依赖符号链接,我也不能再使用 PYTHONPATH 变量了。

3 个回答

0

包其实就是命名空间里的条目。你在命名路径的时候,最好用合法的Python变量名,不要用其他不合规的名字。

10

你可以给导入的模块起不同的名字,而不是使用 imp

import mypackage_xxyyzzww as mypackage

如果你在 Storage 文件夹里创建一个 __init__.py 文件,你可以添加上面几行代码,这样导入的时候会更方便。

Storage/__init__.py:

import mypackage_xxyyzzww as mypackage
import otherpackage_xxyyzzww as otherpackage

解释器:

>>> from Storage import mypackage, otherpackage
4

importlib 可能更合适,因为它使用了PEP302机制。

可以参考 DictImporter 的例子,但要重写 find_module 方法,以找到真实的文件名并把它存储在字典里,然后重写 load_module 方法,从找到的文件中获取代码。

一旦你创建了你的 Storage 模块,就不需要再使用 sys.path 了。

#from importlib import abc
import imp
import os
import sys
import logging
logging.basicConfig(level=logging.DEBUG)

dprint = logging.debug


class MyImporter(object):
    def __init__(self,path):
        self.path=path
        self.names = {}

    def find_module(self,fullname,path=None):
        dprint("find_module({fullname},{path})".format(**locals()))
        ml = imp.find_module(fullname,path)
        dprint(repr(ml))
        raise ImportError


    def load_module(self,fullname):
        dprint("load_module({fullname})".format(**locals()))
        return imp.load_module(fullname)
        raise ImportError


def load_storage( path, modname=None ):
    if modname is None:
        modname = os.path.basename(path)

    mod = imp.new_module(modname)
    sys.modules[modname] = mod
    assert mod.__name__== modname
    mod.__path__=[path]
    #sys.meta_path.append(MyImporter(path))
    mod.__loader__= MyImporter(path)
    return mod

if __name__=="__main__":
    load_storage("arbitrary-path-to-code/Storage")

    from Storage import plain
    from Storage import mypkg

然后当你 import Storage.mypackage 时,Python 会立即使用你的导入器,而不需要去查找 sys.path

这不行。上面的代码确实可以在 Storage 下导入普通模块,而不需要 Storage 在 sys.path 上,但 3.1 和 2.6 似乎都忽略了 PEP302 中提到的 loader 属性。如果我取消注释 sys.meta_path 这一行,3.1 会出现 StackOverflow 错误,而 2.6 会出现 ImportError 错误。唔……我现在没时间了,但可能会稍后再看看。

撰写回答