Python'da bir ad alanı paketini nasıl oluştururum?


141

Python'da, bir ad alanı paketi Python kodunu çeşitli projeler arasında yaymanıza izin verir. Bu, ilgili kütüphaneleri ayrı indirmeler olarak yayınlamak istediğinizde kullanışlıdır. Örneğin, dizinlerde Package-1ve Package-2içinde PYTHONPATH,

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

son kullanıcı import namespace.module1ve import namespace.module2.

Bir ad alanı paketini tanımlamanın en iyi yolu nedir, böylece birden fazla Python ürünü bu ad alanındaki modülleri tanımlayabilir?


5
Bana modül1 ve modül2 aslında modüller yerine alt paketler gibi görünüyor. Anladığım kadarıyla, bir modül temel olarak tek bir dosyadır. Belki subpkg1 ve subpkg2 isimler olarak daha anlamlı olur?
Alan

Yanıtlar:


79

TL; DR:

Python 3.3'te hiçbir şey yapmanız gerekmiyor, sadece __init__.pyad alanı paket dizinlerinize herhangi bir şey koymayın ve sadece işe yarayacak. 3.3 öncesi sürümde, pkgutil.extend_path()çözümü pkg_resources.declare_namespace()geleceğe göre seçin ve örtük ad alanı paketleriyle zaten uyumludur.


Python 3.3 kapalı ad alanı paketleri sunar, bkz. PEP 420 .

Bu, şu anda bir tarafından oluşturulabilecek üç tür nesne olduğu anlamına gelir import foo:

  • Bir foo.pydosya ile temsil edilen modül
  • Dosya fooiçeren bir dizinle temsil edilen normal bir paket__init__.py
  • Bir veya daha fazla dizinleri ile temsil edilen bir ad paketi fooherhangi olmadan __init__.pydosyaları

Paketler de modüller, ama burada "modül" dediğimde "paket dışı modül" demek istiyorum.

İlk sys.patholarak bir modül veya normal paket tarar . Başarılı olursa aramayı durdurur ve modülü veya paketi oluşturur ve başlatır. Herhangi bir modül veya normal paket bulamadıysa, ancak en az bir dizin bulduysa, bir ad alanı paketi oluşturur ve başlatır.

Modüller ve normal paketler , oluşturuldukları dosyaya __file__ayarlanmıştır .py. Normal ve ad alanı paketleri __path__, oluşturuldukları dizine veya dizinlere ayarlanmıştır.

Bunu yaptığınızda import foo.bar, yukarıdaki arama önce gerçekleşir foo, ardından bir paket bulunursa, arama yerine arama yolu olarak baryapılır . Eğer bulunursa ve oluşturulur ve başlatılırsa.foo.__path__sys.pathfoo.barfoofoo.bar

Peki, normal paketler ve ad alanı paketleri nasıl karışır? Normalde yoktur, ancak eski pkgutilaçık ad alanı paketi yöntemi örtük ad alanı paketlerini içerecek şekilde genişletilmiştir.

Şuna benzer mevcut bir normal paketiniz varsa __init__.py:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

... eski davranış, aranan yola başka düzenli paketleri eklemektir .__path__ . Ancak Python 3.3'te ad alanı paketleri de ekler.

Böylece aşağıdaki dizin yapısına sahip olabilirsiniz:

├── path1
   └── package
       ├── __init__.py
       └── foo.py
├── path2
   └── package
       └── bar.py
└── path3
    └── package
        ├── __init__.py
        └── baz.py

... ve sürece iki yanı __init__.pyvar extend_pathhatları (ve path1, path2ve path3Gözlerinde farklı olan sys.path) import package.foo, import package.barve import package.baztüm çalışma olacak.

pkg_resources.declare_namespace(__name__) örtük ad alanı paketleri içerecek şekilde güncellenmedi.


2
Kurulum araçları ne olacak? namespace_packagesSeçeneği kullanmak zorunda mıyım? Peki ya __import__('pkg_resources').declare_namespace(__name__)?
Kawing-Chiu

3
Ben eklemeli miyim namespace_packages=['package']içinde setup.py?
Laurent LAPORTE

1
@ clacke: ile namespace_packages=['package'], setup.py namespace_packages.txtEGG-INFO'ya a ekler . Hala etkileri bilmiyorum…
Laurent LAPORTE

1
@ kawing-chiu faydası pkg_resources.declare_namespaceüzerinde pkgutil.extend_patho izlemeye devam edecektir sys.path. Bu şekilde, sys.pathad alanındaki bir paket ilk yüklendikten sonra yeni bir öğe eklenirse , bu yeni yol öğesindeki ad alanındaki paketler yine de yüklenebilir. (Kullanmanın bir yararı __import__('pkg_resources')üzerinde import pkg_resourcessonun kalmamasıdır pkg_resourcesolduğunun ortaya konulması my_namespace_pkg.pkg_resources.)
Arthur Tacca

1
@clacke Bu şekilde çalışmaz (ancak sanki aynı etkiye sahiptir). Bu işlevle oluşturulan tüm paket ad alanlarının genel listesini tutar ve izler sys.path. Ne zaman sys.patho etkiliyorsa o çekleri değiştiren __path__herhangi ad alanının ve daha sonra eğer bu o günceller __path__özellikleri.
Arthur Tacca

81

Belirli bir ad alanına 'ekleyebileceğiniz' pkgutil adlı standart bir modül vardır .

Sağladığınız dizin yapısı ile:

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

Her iki bu iki satırı koymalıyız Package-1/namespace/__init__.pyve Package-2/namespace/__init__.py(*):

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

(* -onlar arasında bir bağımlılık belirtmedikçe- hangilerinin önce tanınacağını bilmiyorsunuz - daha fazla bilgi için PEP 420'e bakın )

Gibi belgeler diyor ki:

Bu, paketin adlandırılmış olduğu paketin __path__tüm dizin dizinlerini ekler sys.path.

Şu andan itibaren, bu iki paketi bağımsız olarak dağıtabilmelisiniz.


17
İmport __ ('pkg_resources'). Declare_namespace (__ name ) yerine bunu kullanmanın avantajları ve dezavantajları nelerdir?
joeforker

14
Birincisi, __import__basit ithalat ifadesi ile kolayca değiştirilebileceği için bu durumda kötü stil olarak kabul edilir. Daha da önemlisi, pkg_resources standart olmayan bir kütüphanedir. Setuptools ile birlikte geliyor, bu yüzden bu bir sorun değil. Hızlı googling, pkgutil'in 2.5'te tanıtıldığını ve pkg_resources'un önüne geçtiğini ortaya koyuyor. Bununla birlikte, pkgutil resmi olarak tanınan bir çözümdür. pkg_resources katılımı aslında PEP 365'te reddedildi.
Mike Hordecki

3
PEP 382'den alıntı: Ad alanı paketlerine yönelik şu andaki zorunlu yaklaşım, ad alanı paketleri sağlamak için biraz uyumsuz mekanizmalara yol açmıştır. Örneğin, pkgutil * .pkg dosyalarını destekler; setuptools bunu yapmaz. Benzer şekilde, setuptools zip dosyalarının incelenmesini destekler ve _namespace_packages değişkenine kısım eklemeyi destekler, ancak pkgutil desteklemez.
Drake Guan

7
Bu iki satır her iki dosyaya da yerleştirilmemelidir: Package-1/namespace/__init__.py ve Package-2/namespace/__init__.py ilk olarak hangi Paket dizininin listelendiğini bilmememiz gerekir mi?
Bula

3
@ChristofferKarlsson evet bu nokta, hangisinin ilk olduğunu biliyorsanız sorun değil, ama asıl soru, herhangi bir durumda, yani diğer kullanıcılar için ilk olacağını garanti edebilir misiniz?
Bula


2

Bu eski bir sorudur, ancak yakın zamanda birisi blogumda ad alanı paketleri hakkındaki yayınımın hala alakalı olduğunu yorumladı, bu yüzden buraya nasıl gideceğime dair pratik bir örnek sağladığı için buraya bağlayacağımı düşündüm:

https://web.archive.org/web/20150425043954/http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

Bu, neler olup bittiğinin ana cesaretleri için bu makaleye bağlantı veriyor:

http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package

__import__("pkg_resources").declare_namespace(__name__)Hile hemen hemen sürücüler içinde eklentileri yönetimidir TiddlyWeb ve böylece uzaklarda çalışıyor gibi görünüyor.


-9

Python ad alanı konseptleriniz öne çıkıyor, python'da paketleri modüllere koymak mümkün değil. Paketler tam tersi olmayan modüller içerir.

Python paketi sadece __init__.pydosya içeren bir klasördür . Modül, paketteki (veya doğrudan üzerindeki PYTHONPATH) .pyuzantıya sahip başka bir dosyadır . Yani örneğinizde iki paketiniz var, ancak tanımlanmış modül yok. Bir paketin bir dosya sistemi klasörü ve bir modülün dosya olduğunu düşünüyorsanız, neden paketlerin neden modül içerdiğini görürsünüz.

Örneğin, Paket-1 ve Paket-2'nin Python yoluna koyduğunuz dosya sistemindeki klasörler olduğunu varsayarak, aşağıdakilere sahip olabilirsiniz:

Package-1/
  namespace/
  __init__.py
  module1.py
Package-2/
  namespace/
  __init__.py
  module2.py

Artık namespaceiki modüllü bir paketiniz var module1ve module2. ve iyi bir nedeniniz yoksa modülleri klasöre koymanız ve yalnızca aşağıdaki gibi python yolunda olmanız gerekir:

Package-1/
  namespace/
  __init__.py
  module1.py
  module2.py

zope.xBir sürü ilgili paketin ayrı indirmeler olarak yayınlandığı yerler hakkında konuşuyorum .
joeforker

Tamam, ama elde etmeye çalıştığınız etki nedir? PYTHONPATH'da ilgili paketleri içeren klasörler Python yorumlayıcısı sizin için fazladan çaba harcamadan bu dosyaları sizin için bulur.
Tendayi Mawushe

5
PYTHONPATH öğesine hem Paket-1 hem de Paket-2'yi eklerseniz, Python tarafından yalnızca Paket-1 / ad alanı / görünür.
Søren Løvborg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.