2015-09-30 15 views
5

Mam projektu Django skonstruowany tak:import wewnątrz opakowania nie zawsze pracują

appname/ 
    models/ 
     __init__.py 
     a.py 
     base.py 
     c.py 

... gdzie appname/models/__ init__.py zawiera jedynie oświadczenia tak:

from appname.models.base import Base 
from appname.models.a import A 
from appname.models.c import C 

... i gdzie appname/models/base.py zawiera:

import django.db.models 


class Base(django.db.models.Model): 
    ... 

i gdzie appname/models/a.py zawiera:

import appname.models as models 


class A(models.Base): 
    .... 

... i podobnie dla appname/models/c.py, etc ..

Jestem bardzo zadowolony z tej struktury mojego kodu, ale oczywiście to nie działa, ponieważ z okólnikiem import.

Po uruchomieniu appname/__ init__.py zostanie uruchomiony program appname/models/a.py, ale moduł ten zaimportuje "appname.models", które jeszcze nie zostało zakończone. Klasyczny cykliczny import.

Podejrzewam, że mój kod jest źle zbudowany i wymaga przeprojektowania w celu uniknięcia zależności cyklicznej.

Jakie są opcje, aby to zrobić?

Niektóre rozwiązania można myślę i dlaczego nie chcą z nich korzystać:

  1. połączyć wszystkie moje kod modelu w jednym pliku: Mając 20 + zajęcia w tym samym pliku jest znacznie gorzej styl niż to, co próbuję zrobić (z oddzielnymi plikami), moim zdaniem.
  2. Przenieś klasę modelu "Base" do innej paczki poza "appname/models": Oznacza to, że otrzymam pakiet w moim projekcie zawierający klasy podstawowe/nadrzędne, które najlepiej powinny zostać podzielone na pakiety, w których dziecko/podklasy są zlokalizowane. Dlaczego mam mieć klasy bazowe/nadrzędne dla modeli, formularzy, widoków itp. W tym samym pakiecie, a nie w ich własnych pakietach (gdzie dziecko/klasy podrzędne byłyby zlokalizowane), poza unikaniem importu okrężnego?

Moje pytanie dotyczy nie tylko tego, w jaki sposób należy unikać importu okrężnego, ale również w taki sposób, który jest tak samo czysty (jeśli nie czystszy), co próbowałem wdrożyć.

Czy ktoś ma lepszy sposób?

+0

pytania „styl kodowania” są nie na temat: unikać takiego farszu na tytuły (i pytania), ale raczej po prostu przedstawić obiektywne problemu/zagadnienia. – user2864740

+0

Moje pytanie jest związane ze stylem, jak to jest poza tematem? – pleasedesktop

+0

Nie mówi, że twoje pytanie jest nie na temat. Zmienił swój tytuł tak, aby nie wydawał się nie na temat dla osób, które nie czytają uważnie. :-) –

Odpowiedz

4

Edit

mam to bardziej dokładnie zbadane i doszli do wniosku, że jest to problem występujący w obu rdzenia Python lub dokumentacji Pythona. Więcej informacji jest dostępnych at this question and answer.

PEP Pythona 8 wskazuje na wyraźną preferencję dla bezwzględnych względnych względnych importów. Ten problem ma obejście, które obejmuje względny import i istnieje możliwość naprawy w mechanizmie importu.

Moja oryginalna odpowiedź poniżej zawiera przykłady i obejścia.

Original odpowiedź

Problem, jak słusznie wywnioskować, jest wzajemnie od siebie zależnych. W niektórych przypadkach Python może sobie z nimi poradzić, ale jeśli otrzymasz zbyt wiele zagnieżdżonych importów, ma problemy.

Na przykład, jeśli masz tylko jeden pakiet, trudno jest go złamać (bez importów wzajemnych), ale zaraz po zagnieżdżeniu pakietów działa on bardziej jak import wzajemny i zaczyna się stają się trudne do wykonania. Oto przykład, który wywołuje błąd:

level1/__init__.py

from level1.level2 import Base 

level1/level2/__init__.py

from level1.level2.base import Base 
    from level1.level2.a import A 

level1/level2/a.py

import level1.level2.base 
    class A(level1.level2.base.Base): pass 

level1/level2/base

class Base: pass 

Błąd można "naprawić" (dla tego małego przypadku) na kilka różnych sposobów, ale wiele potencjalnych poprawek jest kruchych. Na przykład jeśli nie potrzebujesz importować pliku A w pliku level2 __init__, usunięcie tego importu rozwiąże problem (a twój program może później wykonać import level1.level2.a.A), ale jeśli pakiet stanie się bardziej złożony, zobaczysz, jak błędy pełzają ponownie.

Python czasami wykonuje dobrą robotę, aby ten złożony import działał, a zasady dotyczące tego, kiedy i jak nie będą działać, nie są wcale intuicyjne. Jedną z ogólnych zasad jest to, że from xxx.yyy import zzz może być bardziej wyrozumiały niż import xxx.yyy, a następnie xxx.yyy.zzz. W tym drugim przypadku interpreter musi zakończyć powiązanie yyy z obszarem nazw , gdy nadejdzie czas pobrania xxx.yyy.zzz, ale w pierwszym przypadku interpreter może przemieścić moduły w pakiecie, zanim przestrzeń nazw pakietu najwyższego poziomu zostanie całkowicie ustawiona. w górę.

Więc na tym przykładzie, prawdziwym problemem jest nagi import w a.py To może być łatwo ustalony:

from level1.level2.base import Base 
    class A(Base): pass 

Konsekwentnie używając importu względne jest dobrym sposobem, aby wymusić to wykorzystanie from ... import z tej prostej przyczyny, że import względne nie działają bez from'. To use relative imports with the example above, POZIOM 1/2 Poziom/a.py` powinien zawierać:

from .base import Base 
class A(Base): pass 

To łamie problematyczny cykl import i wszystko działa poprawnie. Jeśli importowany nazwisko (takie jak bazy) jest zbyt łudząco generycznych, gdy nie jest poprzedzona nazwą modułu źródła, można łatwo zmienić jego nazwę na import:

from .base import Base as BaseModel 
class A(BaseModel): pass 

Mimo że rozwiązuje bieżące problemy, jeśli struktura pakietu dostaje więcej skomplikowane, możesz rozważyć ogólne użycie importu względnego.Na przykład, level1/level2/__init__.py mogą być:

from .base import Base 
from .a import A 
+0

Próbuję tego teraz i będę zgłaszać, ale czy istnieje sposób na użycie importu względnego z importem deklaratywnym? – pleasedesktop

+0

@freshquiz - Co masz na myśli? –

+0

Tak więc względne importowanie wydaje się rozwiązać problem, ale najlepiej byłoby powstrzymać się od używania importu "z" stylu w kodzie modułu non \ _ \ _ init \ _ \ _. Py. To nie jest możliwe w przypadku względnego importu? – pleasedesktop

Powiązane problemy