2010-03-17 17 views
7

Po pierwsze: wiem, że istnieje już wiele pytań i odpowiedzi na temat importu okrężnego.Python: wymagany jest cykliczny import sprawdzania typu.

Odpowiedź brzmi mniej więcej: "poprawnie zaprojektuj strukturę modułu/klasy i nie będziesz potrzebować importu cyklicznego". To prawda. Bardzo się starałem, aby stworzyć właściwy projekt dla mojego obecnego projektu. W mojej opinii udało mi się to osiągnąć.

Ale mój konkretny problem jest następujący: Potrzebuję kontroli typu w module, który jest już zaimportowany przez moduł zawierający klasę do sprawdzenia. Ale to powoduje błąd importu.

tak:

foo.py:

from bar import Bar 

class Foo(object): 

    def __init__(self): 
     self.__bar = Bar(self) 

bar.py:

from foo import Foo 

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     if not isinstance(arg_instance_of_foo, Foo): 
      raise TypeError() 

Rozwiązanie 1: Gdybym zmodyfikowano go do sprawdzenia typu przez porównanie smyczkową , to będzie działać. Ale nie podoba mi się to rozwiązanie (porównywanie ciągów jest dość kosztowne w przypadku prostego sprawdzenia typu i może spowodować problem z refaktoryzacją).

bar_modified.py:

from foo import Foo 

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     if not arg_instance_of_foo.__class__.__name__ == "Foo": 
      raise TypeError() 

Rozwiązanie 2: mogę również zapakować dwie klasy w jednym module. Ale mój projekt ma wiele różnych klas, takich jak przykład "Bar", i chcę je oddzielić na różne pliki modułów.

Po tym, jak moje własne 2 rozwiązania nie są dla mnie rozwiązaniem: czy ktoś ma lepsze rozwiązanie tego problemu?

+5

Jak na temat korzystania kaczka wpisywanie? –

+0

Zobacz https://stackoverflow.com/questions/39740632/python-type-hinting-without-cicult-imports dla poprawnej odpowiedzi. –

Odpowiedz

4

Najlepszym rozwiązaniem jest nie sprawdzanie typów.

Drugim rozwiązaniem jest nie tworzenie instancji, a nie odwoływanie się do niej, dopóki nie zostaną załadowane obie klasy. Jeśli pierwszy moduł zostanie załadowany jako pierwszy, nie twórz modelu Bar ani nie odsyłaj do Bar, dopóki nie zostanie wykonane polecenie Bar. Podobnie, jeśli drugi moduł jest ładowany jako pierwszy, nie należy tworzyć wartości Foo ani odniesienia Foo, dopóki nie zostanie wykonane polecenie class Bar.

To jest w zasadzie źródłem ImportError, których można by uniknąć, jeśli nie „import” i „foo bar” zamiast importu i wykorzystywane foo.Foo gdzie teraz używać Foo i bar.Bar gdzie teraz używać Bar. Robiąc to, nie odwołujesz się do żadnego z nich, dopóki nie zostanie utworzony Foo lub Bar, co, miejmy nadzieję, nie nastąpi, dopóki oba nie zostaną utworzone (inaczej otrzymasz AttributeError).

2

Można tylko odroczyć import w bar.py takiego:

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     from foo import Foo 
     if not isinstance(arg_instance_of_foo, Foo): 
      raise TypeError() 
+1

Wszystkie towary importowane powinny znajdować się na górze, chyba że istnieje * naprawdę * dobry powód, aby tego nie robić. To nie jest to. –

+0

@Devin: Twoja odpowiedź jest lepsza, ale jest to standardowy sposób obsługi okrężnych importów, więc odpowiedź jest nadal opłacalna. – FogleBird

+0

@Devin Jeanpierre: Co jeszcze mogłem zrobić? –

4

Można zaprogramować przeciwko interface (ABC - abstract base class w python), a nie specyficzny rodzaj Bar. Jest to klasyczny sposób rozwiązywania zależności między pakietami i modułami w wielu językach.Konceptualnie powinno to również skutkować lepszym projektowaniem modelu obiektowego.

W twoim przypadku zdefiniujesz interfejs IBar w innym module (lub nawet w module zawierającym klasę Foo - w zależności od użycia tego abc). Ty wtedy kod wygląda następująco:

foo.py:

from bar import Bar, IFoo 

class Foo(IFoo): 
    def __init__(self): 
     self.__bar = Bar(self) 

# todo: remove this, just sample code 
f = Foo() 
b = Bar(f) 
print f 
print b 
x = Bar('do not fail me please') # this fails 

bar.py:

from abc import ABCMeta 
class IFoo: 
    __metaclass__ = ABCMeta 

class Bar(object): 
    def __init__(self, arg_instance_of_foo): 
     if not isinstance(arg_instance_of_foo, IFoo): 
      raise TypeError() 
+1

>> :) Wystarczająco fair. W Javie i innych silnych językach maszynowych takie problemy pojawiają się dość często i mają pewne "standardowe" rozwiązania. To nie jest problem w Pythonie, gdzie pisanie kaczek jest po prostu "drogą". Niemniej jednak założyłem, że gdy pytanie zostanie zadane, istnieje "wymóg" zapewnienia tego typu. – van

Powiązane problemy