Як написати гарний / правильний пакет __init__.py файлів


188

Мій пакет має таку структуру:

mobilescouter/
    __init__.py #1
    mapper/
        __init__.py  #2
        lxml/
            __init__.py #3
            vehiclemapper.py
            vehiclefeaturemapper.py
            vehiclefeaturesetmapper.py
        ...
        basemapper.py
   vehicle/
        __init__.py #4
        vehicle.py
        vehiclefeature.py
        vehiclefeaturemapper.py
   ...

Я не впевнений, як __init__.pyслід правильно записати файли.
У __init__.py #1виглядає наступним чином :

__all__ = ['mapper', 'vehicle']
import mapper
import vehicle

Але як, наприклад, має __init__.py #2виглядати? Моє:

__all__ = ['basemapper', 'lxml']
from basemaper import *
import lxml

Коли слід __all__використовувати?


3
Будьте в курсі, що використання імпорту * в коді, як правило, є дуже поганою практикою, і його слід уникати, якщо можливо. Для цього дуже мало випадків корисного використання, але вони справді рідкісні.
Mayou36

PSA: якщо вам цікаво навчитися писати гарні пакети просторів імен (новий вид пакета), ознайомтеся з цим прикладом пакету: github.com/pypa/sample-namespace-packages
Кайл

Відповіді:


146

__all__дуже добре - допомагає керувати заявами про імпорт без автоматичного імпорту модулів http://docs.python.org/tutorial/modules.html#importing-from-a-package

використання __all__і import *є зайвим, лише __all__потрібне

Я думаю , що одна з найсильніших причин для використання import *в __init__.pyдля імпорту пакетів , щоб мати можливість реорганізувати сценарій , який виріс в декількох сценаріїв , не порушуючи існуючу програму. Але якщо ви розробляєте пакет з самого початку. Я думаю, що найкраще залишити __init__.pyфайли порожніми.

наприклад:

foo.py - contains classes related to foo such as fooFactory, tallFoo, shortFoo

потім додаток росте, і тепер це ціла папка

foo/
    __init__.py
    foofactories.py
    tallFoos.py
    shortfoos.py
    mediumfoos.py
    santaslittlehelperfoo.py
    superawsomefoo.py
    anotherfoo.py

тоді сценарій init може сказати

__all__ = ['foofactories', 'tallFoos', 'shortfoos', 'medumfoos',
           'santaslittlehelperfoo', 'superawsomefoo', 'anotherfoo']
# deprecated to keep older scripts who import this from breaking
from foo.foofactories import fooFactory
from foo.tallfoos import tallFoo
from foo.shortfoos import shortFoo

щоб сценарій, написаний для виконання наступних дій, не порушувався під час зміни:

from foo import fooFactory, tallFoo, shortFoo

3
Мене дуже збентежило " все " та імпорт по рядках. Ваш приклад дуже яскравий.
червня 1616 року

2
Мене бентежить " __all__і import *є зайвим", __all__використовується споживачем модуля, а from foo import *сам модуль використовується для використання інших ....
Nick T

using __all__ and import * is redundant, only __all__ is needed Як це зайве? Вони роблять різні речі.
ендоліти

113

Мої власні __init__.pyфайли частіше порожні. Зокрема, я ніколи не є from blah import *частиною __init__.py- якщо "імпорт пакету" означає отримання всіляких класів, функцій тощо, визначених безпосередньо як частину пакету, то я б лексично копіював вміст blah.pyпакета __init__.pyзамість цього та видаляв blah.py( множення вихідних файлів тут не приносить користі).

Якщо ви наполягаєте на підтримці import *ідіоми (eek), то використання __all__(з мінімальним переліком імен, як ви можете довести себе до цього), може допомогти контролювати шкоду. Взагалі простори імен та явний імпорт - це хороші речі, і я настійно пропоную переглянути будь-який підхід, який базується на систематичному обході одного чи обох понять! -)


9
Особисто я вважаю за краще тримати речі окремо, а потім імпортувати *. Причина в тому, що, незважаючи на складання та інші матеріали, я все ще ненавиджу переглядати файли, що містять занадто багато класів, навіть якщо вони пов'язані.
Стефано Борині

5
@stefano подумайте про великий рамки. якщо він використовується, import *ви повинні беззастережно приймати всі рамки в усіх своїх, навіть функціях, які ви ніколи не будете використовувати. збереження __init__.pyпорожнього дасть вам більше шансів, ніж просто семантичне все-чи нічого. подумайте про скручене.
мг.

якщо залишити його порожнім, навіть після імпорту mobilescouter, все одно не можна використовувати mobilescouter.mapper або mobilescouter.vehicle або mobilescouter.white. не імпортувати mobilescouter.A, mobilescouter.B ..... занадто багатослівний?
sunqiang

6
@sunqiang це особисте, але я не думаю, що так. from mobilescouter import A, Bце лише рядок коду, і у вас немає проекту з 666 класами, і кожен має свій власний файл, правда? якщо import *у коді є дві або більше, ви заповнюєте простір імен потенційним сміттям і швидко забудете, звідки Aберуться. І якщо верхній пакет зробити те саме? ви захоплюєте всі підпакети та підпакети. як каже дзен python, явне краще, ніж неявне.
мг.

1
@mg, якщо у файлі init .py є рядок "імпортувати A, B" , я можу викликати A (або B) із синтаксисом: mobilescouter.A; якщо ми використовуємо "з імпорту мобільного супроводу A, B", то це просто А. щось. колись якраз цей рядок, я не пам’ятаю A - це субпакет mobilescouter, і я думаю, що це сприяє забрудненню простору імен (хоча це набагато краще, ніж "" з імпорту mobilescouter * ". Я все ще віддаю перевагу" імпортувати pkgname ", що дають користувачеві єдиний загальнодоступний інтерфейс. так init .py роби імпорт sub_pkgname речі.
sunqiang

1

У вас __init__.pyмає бути докстринг .

Незважаючи на те, що вся функціональність реалізована в модулях і підпакетах, ваша документація пакетів - це місце для документування, з чого почати. Наприклад, розглянемо пакет pythonemail . Документація щодо пакета - це вступ, що описує мету, передумови та спосіб взаємодії різних компонентів у пакеті. Якщо ви автоматично генеруєте документацію з docstrings, використовуючи сфінкс або інший пакет, docstring пакету є саме правильним місцем для опису такого вступу.

Будь-який інший вміст дивіться у відмінні відповіді firecrow та Alex Martelli .


Чи відповідає фактичне __init__.pyдля emailупаковки це керівництво? Я бачу однорядковий docstring, який не дуже допомагає пояснити "як різні компоненти в пакеті працюють разом".
Гертлекс

@Gertlex Можливо лише у веб-документації.
Герріт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.