2009-02-18 9 views
18

Piszę oprogramowanie na Githubie. Jest to w zasadzie ikona z dodatkowymi funkcjami. Chcę dostarczyć działający fragment kodu bez konieczności instalowania użytkownika, co jest w istocie zależnościami dla opcjonalnych funkcji i nie chcę importować rzeczy, których nie zamierzam używać, więc pomyślałem, że kod taki będzie " dobre rozwiązanie ":Co to jest dobra praktyka Pythona do importowania i oferowania opcjonalnych funkcji?

---- IN LOADING FUNCTION ---- 
features = [] 

for path in sys.path: 
     if os.path.exists(os.path.join(path, 'pynotify')): 
       features.append('pynotify') 
     if os.path.exists(os.path.join(path, 'gnomekeyring.so')): 
       features.append('gnome-keyring') 

#user dialog to ask for stuff 
#notifications available, do you want them enabled? 
dlg = ConfigDialog(features) 

if not dlg.get_notifications(): 
    features.remove('pynotify') 


service_start(features ...) 

---- SOMEWHERE ELSE ------ 

def service_start(features, other_config): 

     if 'pynotify' in features: 
       import pynotify 
       #use pynotify... 

Istnieje jednak kilka problemów. Jeśli użytkownik formatuje swój komputer i instaluje najnowszą wersję swojego systemu operacyjnego, a następnie ponownie uruchamia tę aplikację, funkcje nagle znikają bez ostrzeżenia. Rozwiązaniem jest przedstawienie tego w oknie konfiguracji:

if 'pynotify' in features: 
    #gtk checkbox 
else: 
    #gtk label reading "Get pynotify and enjoy notification pop ups!" 

Ale jeśli jest to powiedzieć, Mac, skąd mam wiedzieć, nie jestem wysyłania użytkownikowi na dzikie gęsi pościg szuka uzależnienia oni nigdy nie może napełnić?

Drugim problemem jest:

if os.path.exists(os.path.join(path, 'gnomekeyring.so')): 

problem. Czy mogę mieć pewność, że plik jest zawsze nazywany gnomekeyring.so we wszystkich dystrybucjach Linuksa?

W jaki sposób inne osoby testują te funkcje? Problem z podstawowego

try: 
    import pynotify 
except: 
    pynotify = disabled 

jest to, że kod ma charakter globalny, to może być pełno dookoła, a nawet jeśli użytkownik nie chce pynotify .... to i tak załadowany.

Co zatem ludzie uważają za najlepszy sposób rozwiązania tego problemu?

Odpowiedz

10

Możesz chcieć rzucić okiem na imp module, który zasadniczo robi to, co robisz ręcznie powyżej. Możesz więc najpierw wyszukać moduł z find_module(), a następnie załadować go poprzez load_module() lub po prostu zaimportować go (po sprawdzeniu konfiguracji).

I tak przy okazji, jeśli używam oprócz: zawsze dodawałem wyjątek (tutaj ImportError), aby nie przypadkowo złapać niepowiązanych błędów.

36

Metoda try: nie musi być globalna - może być używana w dowolnym zakresie, więc moduły mogą być "leniwie załadowane" w środowisku wykonawczym. Na przykład:

def foo(): 
    try: 
     import external_module 
    except ImportError: 
     pass 

    if external_module: 
     external_module.some_whizzy_feature() 
    else: 
     print "You could be using a whizzy feature right now, if you had external_module." 

Gdy skrypt jest uruchamiany, nie podjęta zostanie próba załadowania external_module. Po raz pierwszy foo() jest wywoływana, external_module jest (jeśli jest dostępna) ładowana i wstawiana do lokalnego zakresu funkcji. Kolejne wywołania do foo() reinsert external_module w jego zasięgu bez potrzeby ponownego ładowania modułu.

Ogólnie rzecz biorąc, najlepiej pozwolić, aby Python obsługiwał logikę importu - robił to przez jakiś czas. :-)

+8

Myślę, że blok "exceptException" musi ustawić 'external_module = None' lub otrzymasz' NameError' przy próbie dostępu do niego w bloku if. – abhishekmukherg

+0

Podobnie jak, spróbuj: z wyjątkiem: (bez typu wyjątku), jest to wzorzec przeciwny, przechwytywanie ImportError może nie być tym, czego potrzebujesz. Jeśli moduł istnieje, ale zgłasza wyjątek, złapiesz go i kontynuujesz, ukrywając błąd. Znacznie lepiej jest najpierw sprawdzić istnienie modułu, zdecydować, czy go zaimportować, a następnie * nie * obsłużyć ImportError – user48956

0

Jednym ze sposobów radzenia sobie z problemem różnych zależności dla różnych funkcji jest implementacja opcjonalnych funkcji jako wtyczek. W ten sposób użytkownik ma kontrolę nad tym, które funkcje są aktywowane w aplikacji, ale nie jest odpowiedzialny za samo śledzenie zależności. To zadanie zostanie następnie przetworzone w momencie instalacji każdej wtyczki.