如何只导入包中没有exec __init__.py的子模块

时间:2014-01-23 03:35:12

标签: python import module package

从包中导入子模块时,包文件夹中的__init__.py文件将首先执行exec,如何禁用它。有时我只需要一个包中的一个函数,导入整个包有点重。

例如,pandas.util.clipboard模块不依赖于pandas中的任何其他函数。

from pandas.util.clipboard import clipboard_get将导入该函数,但也导入所有pandas常用模块。是否有一些方法只导入剪贴板模块,因为它是我自己的应用程序文件夹中的模块。

4 个答案:

答案 0 :(得分:6)

没有,按设计。如果要在导入子模块时避免太多开销,只需使用空__init__.py来定义包。通过这种方式,导入包的开销几乎为零。

如果pandas 这样做,则没有方式导入pandas.util.clipboard而无需导入pandas和{{1}首先您可以做什么,但它是一个巨大的 hack 并且等效,是将util模块导入为正常模块而不是作为子模块。您只需找到安装clipboard的位置(例如pandas),然后在/usr/lib/pythonX.Y/dist-packages/sys.path中插入父包的路径)。然后,您可以通过执行以下操作导入/usr/lib/pythonX.Y/dist-packages/pandas/util包:

clipboard

但请注意:

import clipboard

将打印import clipboard from pandas.util import clipboard as clipboard2 print(clipboard == clipboard2) 。实际上这样做可能会破坏很多代码,因为你从根本上打破了False机制所假设的一些不变量。

特别是如果子模块使用相对导入引用其他子模块,则导入将失败,并且在其他情况下它将无法正常运行。失败的另一个例子是你必须处理腌制对象。如果您使用导入为import的模块对某些对象进行了腌制,则可以使用上面导入的模块pandas.util.clipboard对其进行去除。

总之,不要!我建议要么:

  • 如果导入软件包的时间不是真正的问题,请使用它。
  • 或:尝试搜索替代品。如果您只需要clipboard但不需要pandas.util.clipboard的其余部分,那么您首先不应该使用pandas,而应使用仅实现pandas功能的较小包}。

如果查看pandas.util.clipboard source code,您会发现它实际上只是pyperclip模块版本1.3。您只需在clipboard中添加此模块,然后使用它而不是site-packages提供的模块。实际上pandas团队只在源代码的末尾添加了以下部分:

pandas

扩展一下为什么 python import以这种方式工作。

正如您所知,在python中模块是对象。而且,包是模块也会发生,尽管不是每个模块都是一个包。导入包时如下:

## pandas aliases
clipboard_get = paste
clipboard_set = copy

Python必须:

  1. 创建import pandas.util.clipboard 实例module
  2. 创建pandas实例module并将其作为属性添加到util
  3. 创建pandas实例module并将其作为属性添加到clipboard
  4. 为了创建util实例,python 必须在模块中执行代码

    表单的导入:

    module

    只是语法糖:

    from pandas.util import clipboard
    

    请注意,在import pandas.util.clipboard clipboard = pandas.util.clipboard del pandas.util 案例中,from可以是clipboard /包,也可以是module内定义的内容。为了检查这一点,解释器必须还导入util并执行此操作,必须也导入util

答案 1 :(得分:4)

我找到了一个使用sys.meta_path挂钩导入过程的方法:

import imp, sys

class DummyLoader(object):

    def load_module(self, name):
        names = name.split("__")
        path = None
        for name in names:
            f, path, info = imp.find_module(name, path)
            path = [path]
        return imp.load_module(name, f, path[0], info)

    def find_module(self, name, path=None):
        if "__" in name and not name.startswith("__"):
            return DummyLoader()
        else:
            return None

if not sys.meta_path:
    sys.meta_path.append(DummyLoader())
else:
    sys.meta_path[0] = DummyLoader()

使用“__”代替“。”仅用于加载文件:

import pandas__util__clipboard as clip

或使用函数加载文件:

import imp

def load_module(name):
    names = name.split(".")
    path = None
    for name in names:
        f, path, info = imp.find_module(name, path)
        path = [path]
    return imp.load_module(name, f, path[0], info)    

clip = load_module("pandas.util.clipboard")

答案 2 :(得分:0)

我尝试过这些方法,但无法让它们发挥作用。显然,通过设计它不应该工作..

如果必须这样做,请在您尝试导入的仓库中创建一个新分支,或初始化仓库:

git checkout -b without_init

..然后删除__init__.py

从您尝试导入的任何地方,您都可以检查Python是否在正确的分支上,如下所示:

import subprocess
print ("Current branch is:", subprocess.check_output(["git rev-parse --abbrev-ref HEAD"], shell=True).strip().decode())

>> without_init

答案 3 :(得分:0)

使用Python3,如果知道文件的路径,则可以在不执行包含文件的目录中执行__init__的情况下导入文件。从[importlib示例]:(How to only import sub module without exec __init__.py in the package

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)