如何将多个python文件组织成一个模块而又不像一个软件包? [英] How to organize multiple python files into a single module without it behaving like a package?

查看:68
本文介绍了如何将多个python文件组织成一个模块而又不像一个软件包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使用__init__.py将多个文件组织到模块中?

Is there a way to use __init__.py to organize multiple files into a module?

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

Reason: Modules are easier to use than packages, because they don't have as many layers of namespace.

通常我会打包,这是我得到的.问题出在包上,导入包"给了我一个空的命名空间.然后,用户必须使用"fromthepackage import *"(不赞成),或者确切知道其中包含的内容,然后手动将其拉入可用的命名空间.

Normally it makes a package, this I get. Problem is with a package, 'import thepackage' gives me an empty namespace. Users must then either use "from thepackage import *" (frowned upon) or know exactly what is contained and manually pull it out into a usable namespace.

我要拥有的是用户导入程序包",并具有漂亮的干净命名空间,如下所示,公开了与项目相关的功能和类以供使用.

What I want to have is the user do 'import thepackage' and have nice clean namespaces that look like this, exposing functions and classes relevant to the project for use.

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

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

The maintainer's job would be to avoid defining the same name in different files, which should be easy when the project is small like mine is.

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

It would also be nice if people can do from doit_stuff import JobInfo and have it retrieve the class, rather than a module containing the class.

如果我所有的代码都在一个巨大的文件中,这很容易,但是当事情开始变大时,我喜欢组织起来.我磁盘上的内容看起来像这样:

This is easy if all my code is in one gigantic file, but I like to organize when things start getting big. What I have on disk looks sort of like this:

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

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

I only separate them so my files aren't huge and unnavigable. They are all related, though someone (possible me) may want to use the classes by themselves without importing everything.

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

I've read a number of suggestions in various threads, here's what happens for each suggestion I can find for how to do this:

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

If I do not use an __init__.py, I cannot import anything because Python doesn't descend into the folder from sys.path.

如果我使用空白的__init__.py ,则当我import doit_tools时,它是一个空的名称空间,其中没有任何内容.我的文件都没有导入,这使得使用起来更加困难.

If I use a blank __init__.py, when I import doit_tools it's an empty namespace with nothing in it. None of my files imported, which makes it more difficult to use.

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

If I list the submodules in __all__, I can use the (frowned upon?) from thing import * syntax, but all of my classes are behind unnecessary namespace barriers again. The user has to (1) know they should use from x import * instead of import x, (2) manually reshuffle classes until they can reasonably obey line width style constraints.

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

If I add from thatfile import X statements to __init__.py, I get closer but I have namespace conflicts (?) and extra namespaces for things I didn't want to be in there. In the below example, you'll see that:

  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保留一些代码,而不是定义新的名称空间.
  1. The class JobInfo overwrote the module object named JobInfo because their names were the same. Somehow Python can figure this out, because JobInfo is of type <class 'doit_tools.JobInfo.JobInfo'>. (doit_tools.JobInfo is a class, but doit_tools.JobInfo.JobInfo is that same class... this is tangled and seems very bad, but doesn't seem to break anything.)
  2. Each filename made its way into the doit_tools namespace, which makes it more confusing to look through if anyone is looking at the contents of the module. I want doit_tools.utility_functions.py to hold some code, not define a new namespace.

.

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"中获得与预期不同的东西:

Also someone importing just the data abstraction class would get something different than they expect when they do 'from doit_tools import JobInfo':

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

instead of:

current_namespace
\
 - JobInfo (class)

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

So, is this just a wrong way to organize Python code? If not, what is a correct way to split related code up but still collect it in a module-like way?

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

Maybe the best case scenario is that doing 'from doit_tools import JobInfo' is a little confusing for someone using the package?

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

Maybe a python file called 'api' so that people using the code do the following?:

import doit_tools.api
from doit_tools.api import JobInfo

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

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

回应评论的示例:

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

Take the following package contents, inside folder 'foo' which is in python path.

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().

another_class and descriptive_name are there cluttering things up, and also have extra copies of e.g. doit() underneath their namespaces.

如果我在名为Data.py的文件中有一个名为Data的类,则当我从数据导入数据"执行操作时,我会遇到名称空间冲突,因为Data是模块Data内当前名称空间中的类,因此也在当前名称空间中. (但是Python似乎可以处理这个问题.)

If I have a class named Data inside a file named Data.py, when I do 'from Data import Data' then I get a namespace conflict because Data is a class in the current namespace that is inside module Data, somehow is also in the current namespace. (But Python seems to be able to handle this.)

推荐答案

您可以做到这一点,但这并不是一个好主意,并且您正在与Python模块/软件包应该工作的方式作斗争.通过在__init__.py中导入适当的名称,可以使它们在程序包名称空间中可访问.通过删除模块名称,可以使它们不可访问. (有关为什么需要删除它们的信息,请参见这个问题).因此,您可以通过以下内容(在__init__.py中)接近所需的内容:

You can sort of do it, but it's not really a good idea and you're fighting against the way Python modules/packages are supposed to work. By importing appropriate names in __init__.py you can make them accessible in the package namespace. By deleting module names you can make them inaccessible. (For why you need to delete them, see this question). So you can get close to what you want with something like this (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']

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

However, this will break subsequent attempts to import package.another_class. In general, you can't import anything from a package.module without making package.module accessible as an importable reference to that module (although with the __all__ you can block from package import module).

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

More generally, by splitting up your code by class/function you are working against the Python package/module system. A Python module should generally contain stuff you want to import as a unit. It's not uncommon to import submodule components directly in the top-level package namespace for convenience, but the reverse --- trying to hide the submodules and allow access to their contents only through the top-level package namespace --- is going to lead to problems. In addition, there is nothing to be gained by trying to "cleanse" the package namespace of the modules. Those modules are supposed to be in the package namespace; that's where they belong.

这篇关于如何将多个python文件组织成一个模块而又不像一个软件包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆