小编典典

如何将多个python文件组织成一个模块而又不像一个软件包?

python

有没有一种方法可以__init__.py将多个文件组织到一个 模块中

原因:模块比软件包更易于使用,因为它们没有太多的命名空间层。

通常情况下,它会打包,这是我得到的。问题出在包上,“导入包”给了我一个空的命名空间。然后,用户必须使用“ fromthepackage 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

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

如果人们能够做到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()

我只将它们分开,所以我的文件不是巨大且不可导航的。它们都是相关的,尽管有人(可能是我)可能想自己使用类而不导入所有内容。

我已经在各个主题中阅读了许多建议,这是我可以找到的每个建议的执行方式:

如果 不使用__init__.py,则无法导入任何内容,因为Python不会从sys.path降入该文件夹。

如果 使用blank__init__.pyimport doit_tools则为空名称空间,其中不包含任何内容。我的文件均未导入,因此使用起来更加困难。

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

如果我在中 添加from thatfile import X语句__init__.py,我会更近一些,但是我有名称空间冲突(?)以及多余的名称空间,用于我不想在其中的东西。在下面的示例中,您将看到:

  1. JobInfo类覆盖了名为JobInfo的模块对象,因为它们的名称相同。Python以某种方式可以弄清楚这一点,因为JobInfo是type <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

另外,仅导入数据抽象类的人将得到与从“从doit_tools导入JobInfo”进行操作时期望的结果不同的东西:

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

instead of:

current_namespace
\
 - JobInfo (class)

那么,这是组织Python代码的错误方法吗?如果不是,那么拆分相关代码但仍以类似模块的方式收集代码的正确方法是什么?

也许最好的情况是,“使用doit_tools import JobInfo进行操作”会使使用该软件包的人感到有些困惑?

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

import doit_tools.api
from doit_tools.api import JobInfo

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

回应评论的示例:

在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

foo/specialcase.py

class SpecialCase:
    pass

foo/more.py

def getSomeStuff():
    pass

class hold_more_data(object):
    pass

foo/stuff.py

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

class dataholder(object):
    pass

做这个:

>>> 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

another_classdescriptive_name在那里杂乱无章,并且在其名称空间下还具有例如doit()的额外副本。

如果我在名为Data.py的文件中有一个名为Data的类,那么当我执行“从数据导入数据”时,我将遇到名称空间冲突,因为数据是模块Data内当前名称空间中的一个类,因此当前名称空间。(但是Python似乎能够处理这个问题。)


阅读 216

收藏
2021-01-20

共1个答案

小编典典

您可以做到这一点,但这并不是一个好主意,您正在与Python模块/软件包应该工作的方式作斗争。通过在其中导入适当的名称,__init__.py可以使它们在包名称空间中可访问。通过删除模块名称,可以使它们不可访问。(有关为什么需要删除它们的信息,请参阅此问题)。这样一来,您可以通过以下方式接近所需的内容__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']

但是,这将中断随后的尝试import package.another_class。通常,您不能从中导入任何内容,package.module而不package.module能将其作为对该模块的可导入引用进行访问(尽管__all__您可以使用阻塞from package import module)。

更一般而言,通过按类/函数划分代码,您正在使用Python包/模块系统。Python模块通常应包含要作为一个单元导入的内容。为了方便起见,直接在顶级包名称空间中导入子模块组件并不少见,但是相反的做法是试图隐藏子模块并
允许通过顶级包名称空间访问其内容,这将导致-
问题。另外,尝试“清理”模块的程序包名称空间没有任何好处。这些模块应该位于程序包名称空间中。那就是他们的归属。

2021-01-20