Есть ли способ использовать __init__.py
для организации нескольких файлов в модуле?
Причина: Модули проще в использовании, чем пакеты, потому что в них не так много слоев пространства имен.
Обычно это делает пакет, который я получаю. Проблема заключается в пакете, "import thepackage" дает мне пустое пространство имен. Затем пользователи должны либо использовать "из импорта пакета" (нахмурившись), либо точно знать, что содержится, и вручную вытащить его в удобное пространство имен.
Я хочу, чтобы пользователь "импортировал пакет" и имел красивые чистые пространства имен, которые выглядят так, раскрывая функции и классы, относящиеся к проекту для использования.
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.
Если я использовать пустой __init__.py
, когда я import doit_tools
это пустое пространство имен, в котором ничего нет. Ни один из моих файлов не импортирован, что затрудняет его использование.
Если я перечислил подмодули в __all__
, я могу использовать синтаксис (нахмурился??) from thing import *
, но все мои классы снова отстают от лишних барьеров пространства имен. Пользователь должен (1) знать, что они должны использовать from x import *
вместо import x
, (2) вручную перетасовывать классы, пока они не смогут разумно подчиняться ограничениям ширины линии.
Если я добавить операторы from thatfile import X
в __init__.py
, я приближаюсь, но у меня есть конфликты пространства имен (?) и дополнительные пространства имен для вещей, которые я не хотел быть там. В приведенном ниже примере вы увидите, что:
- Класс JobInfo перезаписывал объект модуля с именем JobInfo, потому что их имена были одинаковыми. Как-то Python может это понять, потому что JobInfo имеет тип
<class 'doit_tools.JobInfo.JobInfo'>
. (doit_tools.JobInfo - это класс, но doit_tools.JobInfo.JobInfo - это тот же класс... это запутано и кажется очень плохим, но, похоже, ничего не сломает.) - Каждое имя файла пробилось в пространство имен 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? Если нет, то какой правильный способ разделить связанный код вверх, но все же собрать его модульным способом?
Возможно, лучший сценарий заключается в том, что выполнение 'from doit_tools import JobInfo' немного сбивает с толку для кого-то, использующего пакет?
Может быть, файл python с именем "api", чтобы люди, использующие код, выполняли следующие действия:
import doit_tools.api
from doit_tools.api import JobInfo
============================================р >
Примеры в ответ на комментарии:
Возьмите следующее содержимое пакета, внутри папки 'foo', которая находится в пути python.
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_class
и descriptive_name
существуют беспорядочные вещи, а также дополнительные копии, например. doit() под их пространствами имен.
Если у меня есть класс с именем Data внутри файла с именем Data.py, когда я делаю "из Data Import Data", тогда я получаю конфликт пространства имен, потому что Data - это класс в текущем пространстве имен, который находится внутри модуля Data, так или иначе также в текущем пространстве имен. (Но Python, похоже, справляется с этим.)