【问题标题】:How do I import a function into a Python package without also importing the file that contains it?如何在不导入包含它的文件的情况下将函数导入 Python 包?
【发布时间】:2015-08-20 19:23:05
【问题描述】:

我在 Mac OSX 上使用 Python 3.4.2,我有一个简单的版本控制 Python 项目,其目录/文件结构如下所示:

vcs_projectname/
    foo/
        __init__.py
        simplefunc.py
    docs/
    other_related_stuff/

__init__.py 文件如下所示:

from .simplefunc import helloworld
__all__ = ['helloworld'] # Not sure whether I really need this line...?

simplefunc.py 文件如下所示:

def helloworld():
    print('Hello world!')

我通过更改到项目层次结构之外的目录来测试我的代码,将我的 PYTHONPATH 环境变量(在 bash 中)设置为指向 vcs_projectname 基本目录,然后启动 ipython

> cd ~
> export PYTHONPATH=~/vcs_projectname
> ipython

在 ipython 中,我导入包 foo,然后查看其目录结构,结果如下:

In [1]: import foo

In [2]: dir(foo)
Out[2]: 
['__all__',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 'helloworld',
 'simplefunc']

我的问题:如何去掉包目录结构中对simplefunc文件模块的引用?这是可取的,因为在最好的情况下,它只是无用的混乱(我们不需要它,因为我们真正想要的东西,helloworld() 函数,已经在包级别由@987654334 提供@file),在最坏的情况下,它本质上是对不相关的实现细节(项目的底层文件结构)的引用,以后可能会改变,因此我不希望我的用户期待和依赖未来的版本。

【问题讨论】:

  • 您可以delete 参考。但我不会。
  • 好的,我只是在导入实际完成后在 ipython 命令行 In [3]: del(foo.simplefunc) 上尝试过,从技术上讲它确实有效,但肯定有比这更优雅的解决方案吗?难道没有其他方法可以让我构建__init__.py 文件本身以达到相同的结果,这不仅仅是一个丑陋的组合或解决方法吗?
  • 问题是,你想要完成的最终目标是一个丑陋的组合(在我看来)。您将对用户隐藏源代码结构,这会使调试变得比需要的更难并且非常令人沮丧。使用 _underscore 前缀和 文档 来明确代码的哪些部分是包的公共 API 的一部分,哪些不是。
  • 好的,所以,要明确一点,您建议的解决方案本质上是,如果我想这样做,那么我应该考虑将simplefunc.py 文件重命名为_simplefunc.py 以保留底层源代码文件结构暴露,但仍向用户表明它不打算成为公共 API 的一部分?我是否正确地描述了你的立场?
  • 是的,这就是我的意思,这是清楚地表明您将此模块(名称/位置)视为内部细节的明显方式。但这通常甚至没有必要:如果您在某个时候确实更改了代码布局并移动模块、合并它们等,您始终可以添加内部向后兼容性导入以确保代码适用于直接使用您的“内部”模块的用户仍然可以使用。查看example of that in sqlalchemy

标签: python python-3.x import package python-import


【解决方案1】:

您尝试做的事情不可能优雅地完成。正如@Lukas 提到的,有一些黑客可以做到这一点。

我一直在关注的是,创建一个名为_private 的子包并将所有此类模块放入其中。这样,当用户导入包时,所有暴露的 API 都可用,私有 API 隐藏在 _private 中。

示例:

foo/
    __init__.py
    _private/
        __init__.py
        test1.py
        test2.py

foo/__init__.py:

from _private import bar, baz

foo/_private/__init__.py:

from test1 import bar
from test2 import baz

foo/_private/test1.py:

def bar():
    print "bar"

foo/_private/test2.py:

def baz():
    print "baz"

正在导入foo

>>> import foo
>>> dir(foo)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '_private', 'bar', 'baz']

【讨论】:

    【解决方案2】:

    如何去掉包目录结构中对simplefunc文件模块的引用?

    您可以通过将del simplefunc 添加到您的foo/__init__.py 来实现您的既定目标,如下所示:

    from  .simplefunc import helloworld
    del simplefunc
    __all__ = ['helloworld'] # Not sure whether I really need this line...?
    

    【讨论】:

      猜你喜欢
      • 2017-06-13
      • 1970-01-01
      • 2010-09-30
      • 2021-12-01
      • 2020-04-01
      • 2018-04-09
      • 2020-04-30
      • 1970-01-01
      • 2021-05-07
      相关资源
      最近更新 更多