Jak napisać dobry / poprawny pakiet __init__.py

188

Mój pakiet ma następującą strukturę:

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

Nie jestem pewien, jak __init__.pypliki powinny być poprawnie zapisane.
Do __init__.py #1wygląda następująco:

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

Ale jak na przykład powinien __init__.py #2wyglądać? Mój jest:

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

Kiedy należy __all__stosować?

Marten Bauer
źródło
3
Należy jednak pamiętać, że stosowanie importu * w kodzie jest ogólnie bardzo złą praktyką i należy go w miarę możliwości unikać. Istnieje bardzo niewiele dobrych przypadków użycia, ale są one naprawdę rzadkie.
Mayou36,
PSA: jeśli chcesz nauczyć się pisać dobre pakiety przestrzeni nazw (nowy rodzaj pakietu), sprawdź ten przykładowy pakiet: github.com/pypa/sample-namespace-packages
Kyle

Odpowiedzi:

146

__all__jest bardzo dobry - pomaga poprowadzić instrukcje importowania bez automatycznego importowania modułów http://docs.python.org/tutorial/modules.html#importing-from-a-package

użyciu __all__i import *jest zbędny, tylko __all__jest potrzebna

Myślę, że jednym z najpotężniejszych powodów do użycia import *w __init__.pycelu importowania pakietów jest możliwość refaktoryzacji skryptu, który rozrósł się do wielu skryptów bez zrywania istniejącej aplikacji. Ale jeśli projektujesz pakiet od samego początku. Myślę, że najlepiej zostawić __init__.pypliki puste.

na przykład:

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

wtedy aplikacja rośnie, a teraz jest to cały folder

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

wtedy skrypt init może powiedzieć

__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

aby skrypt napisany w celu wykonania następujących czynności nie ulegał awarii podczas zmiany:

from foo import fooFactory, tallFoo, shortFoo
Fire Crow
źródło
3
Byłem bardzo zdezorientowany co do „ wszystkiego ” i importu wiersz po wierszu. Twój przykład jest bardzo pouczający.
Junchen
2
Jestem zdezorientowany przez „ __all__i import *jest redundantny”, __all__jest używany przez konsumenta modułu i from foo import *jest używany przez sam moduł do korzystania z innych ....
Nick T
using __all__ and import * is redundant, only __all__ is needed Jak się mają zbędne? Robią różne rzeczy.
endolith,
113

Moje własne __init__.pypliki są puste częściej niż nie. W szczególności nigdy nie mam tego from blah import *jako część __init__.py- jeśli „importowanie pakietu” oznacza uzyskanie wszelkiego rodzaju klas, funkcji itp. Zdefiniowanych bezpośrednio jako część pakietu, to zamiast tego skopiowałbym leksykalnie zawartość blah.pypakietu __init__.pyi usunął blah.py( zwielokrotnienie plików źródłowych tutaj nie ma znaczenia).

Jeśli nalegasz na wspieranie import *idiomów (eek), to użycie __all__(z tak niewielką listą imion, jak tylko możesz się w nich znaleźć) może pomóc w kontrolowaniu szkód. Ogólnie rzecz biorąc, przestrzenie nazw i jawne importowanie to dobre rzeczy i zdecydowanie sugeruję ponowne rozważenie każdego podejścia opartego na systematycznym pomijaniu jednego lub obu pojęć! -)

Alex Martelli
źródło
9
Osobiście wolę oddzielić rzeczy, a następnie zaimportować *. Powodem jest to, że pomimo składania i innych rzeczy nadal nie lubię przeglądać plików zawierających zbyt wiele klas, nawet jeśli są one powiązane.
Stefano Borini,
5
@stefano pomyśl o dużym frameworku. jeśli używa import *, musisz bezwarunkowo zaakceptować wszystkie frameworki, nawet te, których nigdy nie będziesz używać. pozostawienie __init__.pypustego daje więcej szans niż tylko semantyczne „wszystko albo nic”. pomyśl o pokręconym.
mg
jeśli pozostanie pusty, nawet po zaimportowaniu mobilescouter, nadal nie można używać mobilescouter.mapper lub mobilescouter.vehicle lub mobilescouter. nie importuje mobilescouter.A, mobilescouter.B ..... zbyt gadatliwy?
sunqiang
6
@sunqiang to jest sprawa osobista, ale nie sądzę. from mobilescouter import A, Bjest tylko wierszem kodu i nie masz projektu z 666 klasami, a każdy z własnym plikiem, prawda? jeśli masz dwa lub więcej import *w kodzie, wypełniasz przestrzeń nazw potencjalnymi śmieciami i szybko zapomnisz, skąd Apochodzą. A jeśli to samo zrobi górna paczka? chwytasz wszystkie sub-paczki i sub-paczki. jak mówi zen Pythona, jawne jest lepsze niż niejawne.
mg
1
@mg, jeśli w pliku .py init znajduje się wiersz „import A, B” , wówczas mogę wywołać A (lub B) ze składnią: mobilescouter.A; jeśli użyjemy „z importu z telefonu komórkowego A, B”, to będzie to po prostu A. coś. kiedyś ta linia, nie pamiętam, że A jest subpakge dla mobilescoutera i myślę, że przyczynia się to do zanieczyszczenia przestrzeni nazw (chociaż jest znacznie lepsze niż „” z importu z telefonu komórkowego * ”. Nadal wolę„ import pkgname ”dać użytkownikowi jednolity interfejs publiczny, więc init .py wykonaj importowanie sub_pkgname rzeczy
sunqiang
1

Twój __init__.pypowinien mieć docstring .

Mimo że cała funkcjonalność jest zaimplementowana w modułach i podpakietach, dokumentowanie pakietu jest miejscem, w którym można rozpocząć dokumentowanie. Rozważmy na przykład pakiet pythonemail . Dokumentacja pakietu jest wstępem opisującym cel, tło oraz sposób współpracy różnych komponentów w pakiecie. Jeśli automatycznie generujesz dokumentację z dokumentów w postaci sfinksa lub innego pakietu, dokumentacja pakietu jest właściwym miejscem do opisania takiego wprowadzenia.

W przypadku innych treści zobacz doskonałe odpowiedzi firecrow i Alex Martelli .

gerrit
źródło
Czy faktyczne informacje __init__.pyo emailpakiecie są zgodne z tymi wytycznymi? Widzę dokumentację w jednym wierszu, która nie robi wiele, aby wyjaśnić „jak różne komponenty w pakiecie działają razem”.
Gertlex
@Gertlex Może tylko w dokumentacji internetowej.
gerrit