如何将多个python文件组织到单个模块中,而不会像包一样?

Bri*_*ian 36 python module package

有没有办法将__init__.py多个文件组织到模块中

原因:模块比包更容易使用,因为它们没有尽可能多的命名空间层.

通常它会生成一个包,我得到了.问题在于包,'import thepackage'给了我一个空命名空间.然后,用户必须使用"from the package import*"(不赞成)或确切知道包含的内容并手动将其拉出到可用的命名空间中.

我想要的是用户"导入包"并拥有看起来像这样的干净名称空间,公开与项目相关的函数和类以供使用.

current_module
\
  doit_tools/
  \
   - (class) _hidden_resource_pool
   - (class) JobInfo
   - (class) CachedLookup
   - (class) ThreadedWorker
   - (Fn) util_a
   - (Fn) util_b
   - (Fn) gather_stuff
   - (Fn) analyze_stuff
Run Code Online (Sandbox Code Playgroud)

维护者的工作是避免在不同的文件中定义相同的名称,这在项目像我的一样小时应该很容易.

如果人们可以做from doit_stuff import JobInfo并让它检索类,而不是包含该类的模块,那也是很好的.

如果我的所有代码都在一个巨大的文件中,这很容易,但我喜欢在事情变得越来越重要时组织起来.我在磁盘上看到的有点像这样:

place_in_my_python_path/
  doit_tools/
    __init__.py
    JobInfo.py
      - class JobInfo:
    NetworkAccessors.py
      - class _hidden_resource_pool:
      - class CachedLookup:
      - class ThreadedWorker:
    utility_functions.py
      - def util_a()
      - def util_b()
    data_functions.py
      - def gather_stuff()
      - def analyze_stuff()
Run Code Online (Sandbox Code Playgroud)

我只将它们分开,所以我的文件不是很庞大且无法通知.它们都是相关的,尽管有人(可能是我)可能想要自己使用这些类而不导入所有内容.

我已经在各种主题中阅读了一些建议,以下是我可以找到的关于如何执行此操作的每个建议:

如果我不使用__init__.py,我无法导入任何东西,因为Python不会从sys.path下载到该文件夹​​中.

如果我使用空白__init__.py,当import doit_tools它是一个没有任何内容的空命名空间时.我的文件都没有导入,这使得它更难以使用.

如果我列出了子模块__all__,我可以使用(皱眉?)from thing import *语法,但我的所有类都在不必要的命名空间障碍后面.用户必须(1)知道他们应该使用from x import *而不是import x(2)手动重新洗牌,直到他们能够合理地服从线宽样式约束.

如果我添加from thatfile import X语句__init__.py,我会更接近,但我有命名空间冲突(?)和额外的命名空间,我不想在那里.在下面的示例中,您将看到:

  1. JobInfo类覆盖了名为JobInfo的模块对象,因为它们的名称相同.不知怎的,Python可以解决这个问题,因为JobInfo属于类型<class 'doit_tools.JobInfo.JobInfo'>.(doit_tools.JobInfo是一个类,但doit_tools.JobInfo.JobInfo是同一个类......这很纠结,看起来非常糟糕,但似乎没有破坏任何东西.)
  2. 每个文件名都进入了doit_tools命名空间,如果有人正在查看模块的内容,这会让人更难以查看.我想要doit_tools.utility_functions.py来保存一些代码,而不是定义一个新的命名空间.

.

current_module
\
  doit_tools/
  \
   - (module) JobInfo
      \
       - (class) JobInfo
   - (class) JobInfo
   - (module) NetworkAccessors
      \
       - (class) CachedLookup
       - (class) ThreadedWorker
   - (class) CachedLookup
   - (class) ThreadedWorker
   - (module) utility_functions
      \
       - (Fn) util_a
       - (Fn) util_b
   - (Fn) util_a
   - (Fn) util_b
   - (module) data_functions
      \
       - (Fn) gather_stuff
       - (Fn) analyze_stuff
   - (Fn) gather_stuff
   - (Fn) analyze_stuff
Run Code Online (Sandbox Code Playgroud)

另外,只是导入数据抽象类的人会得到与他们从'doit_tools导入JobInfo'时所做的不同的东西:

current_namespace
\
 JobInfo (module)
  \
   -JobInfo (class)

instead of:

current_namespace
\
 - JobInfo (class)
Run Code Online (Sandbox Code Playgroud)

那么,这只是组织Python代码的错误方法吗?如果没有,分割相关代码但仍然以模块方式收集它的正确方法是什么?

也许最好的情况是,对于使用该软件包的人来说,"从doit_tools导入JobInfo"进行操作会有点困惑吗?

也许是一个名为'api'的python文件,以便使用该代码的人执行以下操作?:

import doit_tools.api
from doit_tools.api import JobInfo
Run Code Online (Sandbox Code Playgroud)

============================================

回应评论的例子:

在python路径中的文件夹'foo'中取以下包内容.

foo/__init__.py

__all__ = ['doit','dataholder','getSomeStuff','hold_more_data','SpecialCase']
from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
from specialcase import SpecialCase
Run Code Online (Sandbox Code Playgroud)

foo/specialcase.py

class SpecialCase:
    pass
Run Code Online (Sandbox Code Playgroud)

foo/more.py

def getSomeStuff():
    pass

class hold_more_data(object):
    pass
Run Code Online (Sandbox Code Playgroud)

foo/stuff.py

def doit():
    print "I'm a function."

class dataholder(object):
    pass
Run Code Online (Sandbox Code Playgroud)

做这个:

>>> import foo
>>> for thing in dir(foo): print thing
... 
SpecialCase
__builtins__
__doc__
__file__
__name__
__package__
__path__
another_class
dataholder
descriptive_name
doit
getSomeStuff
hold_more_data
specialcase
Run Code Online (Sandbox Code Playgroud)

another_class并且descriptive_name有些混乱,并且在命名空间下面还有例如doit()的额外副本.

如果我在一个名为Data.py的文件中有一个名为Data的类,当我从'数据导入数据'执行时,我得到一个命名空间冲突,因为Data是当前命名空间中的一个类,在模块Data中,不知何故也在当前命名空间 (但Python似乎能够解决这个问题.)

Bre*_*arn 14

你可以这样做,但这并不是一个好主意,你正在与Python模块/包的工作方式作斗争.通过导入适当的名称,__init__.py可以在包命名空间中访问它们.通过删除模块名称,您可以使它们无法访问.(为什么你需要删除它们,请看这个问题).所以你可以用这样的东西接近你想要的东西(in __init__.py):

from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
del another_class, descriptive_name
__all__ = ['doit', 'dataholder', 'getSomeStuff', 'hold_more_data']
Run Code Online (Sandbox Code Playgroud)

但是,这将打破后续尝试import package.another_class.一般情况下,你不能从一个输入任何东西package.module不做package.module访问作为导入的参考该模块(虽然与__all__您可以阻止from package import module).

更一般地说,通过按类/函数拆分代码,您正在使用Python包/模块系统.Python模块通常应包含您要作为一个单元导入的内容.为方便起见,直接在顶级包命名空间中导入子模块组件并不罕见,但反过来 - 试图隐藏子模块并允许通过顶级包命名空间访问其内容- 将导致问题.此外,尝试"清理"模块的包命名空间没有任何好处.那些模块应该在包名称空间中; 这就是他们所属的地方.

  • *'with`__all__`你可以阻止包导入模块'*似乎有误导性`__ all__`不会阻止`来自包导入模块`.它只是控制你从`package import*`那里可用的东西 (4认同)