2010-04-23 16 views
42

Nigdy nie zauważyłem atrybutu __path__, który został zdefiniowany na niektórych moich pakietach przed dzisiejszym dniem. Zgodnie z dokumentacją:Do czego służy funkcja __path__?

pakiety obsługują jedną szczególną cechę , __path__. Jest to zainicjowane jako lista zawierająca nazwę katalogu przechowującego paczkę paczki przed wykonaniem tego kodu w postaci kodu . Ta zmienna może być modyfikowana; robi to wpływa na przyszłe wyszukiwania modułów i podpakietów zawartych w pakiecie .

Podczas gdy ta funkcja często nie jest wymagana, , może być używana do rozszerzenia zestawu modułów o numerze znajdującego się w pakiecie.

Czy ktoś mógłby mi wytłumaczyć, co to dokładnie oznacza i dlaczego chciałbym z niego skorzystać?

Odpowiedz

22

Jest to zwykle używane z pkgutil, aby umożliwić umieszczenie paczki na dysku. Np. Zope.interface i zope.schema to osobne dystrybucje (zope to "pakiet przestrzeni nazw"). Możesz mieć zope.interface zainstalowany w /usr/lib/python2.6/site-packages/zope/interface/, gdy używasz zope.schema bardziej lokalnie w /home/me/src/myproject/lib/python2.6/site-packages/zope/schema.

Jeśli umieścisz pkgutil.extend_path(__path__, __name__) w /usr/lib/python2.6/site-packages/zope/__init__.py następnie zarówno zope.interface i zope.schema będzie importable ponieważ pkgutil będą miały zmiany __path__ do ['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope'].

pkg_resources.declare_namespace (część Setuptools) jest jak pkgutil.extend_path, ale jest bardziej świadomy suwaków na ścieżce.

Ręczna zmiana __path__ jest rzadkością i prawdopodobnie nie jest konieczna, chociaż warto przyjrzeć się zmiennej podczas debugowania problemów związanych z importowaniem pakietów nazw.

Można również użyć __path__ dla monkeypatching np mam monkeypatched distutils w czasach tworząc plik distutils/__init__.py że jest wcześnie sys.path:

import os 
stdlib_dir = os.path.dirname(os.__file__) 
real_distutils_path = os.path.join(stdlib_dir, 'distutils') 
__path__.append(real_distutils_path) 
execfile(os.path.join(real_distutils_path, '__init__.py')) 
# and then apply some monkeypatching here... 
+1

Miałem wrażenie, że ma to coś wspólnego z pakietami przestrzeni nazw, ale miałem problemy ze składaniem razem, jak to działało. Dzięki! –

28

Jeśli zmienisz __path__, możesz wymusić, aby interpreter szukał w innym katalogu modułów należących do tego pakietu.

Umożliwi to np. Ładowanie różnych wersji tego samego modułu w oparciu o warunki środowiska wykonawczego. Możesz to zrobić, jeśli chcesz korzystać z różnych implementacji tej samej funkcjonalności na różnych platformach.

+0

Django używa tej dynamicznie załadować django-admin.py poleceń przy starcie!. 'commands = {nazwa:" django.core "dla nazwy w find_commands (__ ścieżka __ [0])}' –

7

Oprócz wyboru różnych wersji modułu w oparciu o warunki środowiska wykonawczego, jak mówi Syntactic, ta funkcjonalność umożliwiłaby również podzielenie pakietu na wiele części/pobrań/instalacji przy jednoczesnym zachowaniu wyglądu pojedynczego pakietu logicznego.

Należy rozważyć następujące kwestie.

  • mam dwa pakiety, mypkg i _mypkg_foo.
  • _mypkg_foo zawiera opcjonalny moduł do mypkg, foo.py.
  • jako pobrane i zainstalowane, mypkg nie zawiera foo.py.

mypkg „s __init__.py można zrobić coś tak:

try: 
    import _mypkg_foo 
    __path__.append(os.path.abspath(os.path.dirname(_mypkg_foo.__file__))) 
    import mypkg.foo 
except ImportError: 
    pass 

Jeśli ktoś ma zainstalowany pakiet _mypkg_foo, następnie mypkg.foo jest dla nich dostępne. Jeśli nie, to nie istnieje.

0

Szczególna sytuacja Natknąłem jest, gdy pakiet staje się wystarczająco duży, że chcę podzielić jego części na podkatalogi bez konieczności zmiany jakiegokolwiek kodu, który się do nich odwołuje.

Na przykład mam pakiet o nazwie views, który zbierał pewną liczbę pomocniczych funkcji użytkowych, które ulegały pomieszaniu z głównym celem najwyższego poziomu pakietu. I był w stanie przenieść te funkcje wspomagające do podkatalogu utils i dodaj następującą linię do __init__.py dla pakietu views:

__path__.append(os.path.join(os.path.dirname(__file__), "utils")) 

Dzięki tej zmianie zbyt views/__init_.py mogłem uruchomić resztę oprogramowania z nowym pliku struktura bez żadnych dalszych zmian w plikach.

(próbowałem zrobić coś podobnego z import oświadczenia w pliku views/__init__.py, ale moduły sub-pakiet nie były jeszcze widoczne przez import pakietu view - Nie jestem do końca pewien, czy ja czegoś brakuje tam ; komentarze do tego mile widziane)

(tę odpowiedź w oparciu o Pythonie 2.7 instalacja)