Имеют ли пакеты python (многофайловые модули) точно один большой модуль?

Я только что прочитал статью, которая предположительно представила мне новую концепцию: до сих пор я был уверен, что пакеты python (то есть каталоги с файлом __init__.py) ведут себя точно так же, как java-пакеты, то есть - мало пространства имен, которые помогут упорядочить код (минус java "пакет" ). Но, согласно этой ссылке: Короткое отступление в многофайловых модулях, если я поместил все мои файлы в один и тот же "пакет":

вся коллекция файлов представлена ​​в другом коде Python как один модуль - как если бы все функции и классы были в одном .py

Итак, теперь я подумал, что все мое понимание "пакета" python было неправильным. Более того - это полностью не пакет, а скорее "мультифильный модуль", как автор ссылается на него.

Итак, из того, что я понял, независимо от того, сколько файлов я делю мои funcs и классы внутри пакета, снаружи этот пакет должен выглядеть так, как если бы я взял весь код из всех файлов внутри пакета и поместил его в одном большом файле с тем же именем, что и пакет, т.е. как один модуль.

например, если у меня есть следующая файловая структура:

/base
    /animals
        /__init__.py
        /dog.py

и в dog.py:

def bark():
    print "woof"

он должен быть точно таким же, как:

/base
    /animals.py

и в animal.py:

def bark():
    print 'woof'

таким образом, этот следующий фрагмент кода должен работать нормально в обоих случаях:

from base import animals
animals.bark()

Это, конечно, дает в первом случае:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'module' object has no attribute 'bark'

Что мне здесь не хватает? Я вижу, за исключением того, что "животные" действительно рассматриваются как модуль, но, похоже, я все же должен явно указывать animals.dog.bark, то есть внутренняя файловая структура пакета не абстрагируется снаружи.

Я пропущу точку автора или просто не выполнил ее правильно?

=== EDIT ===

Просто чтобы убедиться, что никто не пропустил эту строку в цитате:

как если бы все функции и классы находились в одном .py

независимо от того, как реально получить доступ к этим функциям и классам, приведенная выше цитата явно заявляет, что если у вас есть func1 в файле a и func2 в файле b, независимо от того, с каким путем они будут доступны, если мы обозначим этот путь как X, то, согласно вышеупомянутой цитате, должны работать как X.func1, так и X.func2.

Ответ 1

Автор имеет упрощенные вещи. Он говорит, что все под animal можно рассматривать как находящиеся в одном модуле, хотя факт состоит в том, что имена в animal.dog будут находиться в собственном пространстве имен.

Ответ 2

Возможно, дело просто в том, что пакет - это всего лишь определенный тип модуля.

/base
    /animals
        /__init__.py
        /dog.py

Это означает, что все, что вы определяете или импортируете в __init__.py, будет видно внутри модуля animals.

So animals - это модуль (это пакет), а animals.dog - это модуль, который является подмодулем animals, но не пакетом.

Это также означает, что если у вас есть простой модуль animals, вы можете переустановить его пакетом с тем же именем в следующей версии и организовать так, чтобы ваши пользователи не заметили разницы.

Если вы хотите, чтобы все классы из подмодулей пакета составляли один единственный видимый для пользователя модуль (пространство имен), вы должны определить такую ​​строку для каждого подмодуля в __init__.py:

from animals.dog import *

Ответ 3

Не настоящий ответ, но поскольку мне еще не разрешено прокомментировать (вздох!):

Так как diveintopython много используется/цитируется/ссылается ресурс для программистов Python (по крайней мере, когда они начинаются), вы должны действительно связаться с автором об этом недостатке, поскольку он будет вводить в заблуждение и другим. На домашней странице diveintopython3 есть какая-то контактная информация, и вы также можете указать ее как проблему в github.

Ответ 4

Я не могу объяснить это хорошо, но, возможно, следующий код поможет. Если я оставлю свою первую файловую структуру так, как она есть, и вместо этого изменим вторую, чтобы иметь следующий файл animal.py:

class Dog:
    def bark(self): pass
dog=Dog()

Тогда в обоих случаях

from base import animals
animals.dog.bark()

будет работать.