Yukarıdaki bir dizinde bulunan bir Python sınıfı nasıl alınır?


212

Geçerli olanın üzerinde bir dizinde bulunan bir dosyadaki bir sınıftan devralmak istiyorum.

Bu dosyayı göreceli olarak içe aktarmak mümkün mü?

Yanıtlar:


177

from ..subpkg2 import mod

Python belgelerine göre: Bir paket hiyerarşisinin içindeyken, içe aktarma ifadesi belgesinin dediği gibi iki nokta kullanın :

Hangi modülün içe aktarılacağını belirtirken, modülün mutlak adını belirtmeniz gerekmez. Bir modül veya paket başka bir paket içinde yer alıyorsa, paket adını belirtmek zorunda kalmadan aynı üst paket içinde göreceli bir içe aktarma yapmak mümkündür. fromTam ad belirtmeden geçerli paket hiyerarşisinde ne kadar yukarı geçiş yapılacağını belirledikten sonra, belirtilen modülde veya pakette önde gelen noktaları kullanarak belirtebilirsiniz. Bir öncü nokta, içe aktarmayı yapan modülün bulunduğu geçerli paket anlamına gelir. İki nokta, bir paket seviyesi anlamına gelir . Üç nokta iki seviyeye kadar, vb. Yani paketteki from . import modbir modülden yürütürseniz , pkgo zaman içe aktarma ile sonuçlanacaksınız pkg.mod. Eğer from ..subpkg2 import modiçinden pkg.subpkg1yürütürsenpkg.subpkg2.mod . Göreli ithalat spesifikasyonu,PEP 328 .

PEP 328 , mutlak / göreli ithalat ile ilgilenir.


4
up1 = os.path.abspath ('..') sys.path.insert (0, yukarı1)
rp.

Pep 328 sadece Python-Versiyonunu gösterir: 2.4, 2,5, 2.6. Versiyon 3 daha bilgili ruhlara bırakıldı.
gimel

22
Bu, ValueError hatasını tetikler: üst düzey paketin ötesinde göreceli içe aktarma girişiminde bulundu
Carlo

4
ImportError'daki sonuçlar: bilinen bir üst paket olmadan göreceli içe aktarma girişimi
Rotkiv

116
import sys
sys.path.append("..") # Adds higher directory to python modules path.

1
bu benim için çalışıyor. Bunu ekledikten sonra, ".." kullanmaya gerek olmadan doğrudan ana modülleri alabilirim.
Evan Hu

8
ancak kabaca konuşursak, uygulamanın PWD değeri (şu anki directeroy) ebeveynin çocuğuysa çalışır. Bu nedenle, çalışıyor olsa bile, birçok sistemik değişiklik onu yerinden edebilir.
Phlip

Bu benim için daha yüksek seviyedeki modülleri ithal etmek için de işe yaradı. Bunu daha yüksek seviyedeki diğer dosyaları yüklemek için os.chdir ("..") ile kullanıyorum.
Surendra Shrestha

Bu, gimel'in yukarıdaki cevabı ile aynı hatayı tetikliyor gibi görünüyor.
Carlo

81

@ gimel cevabı doğrudur eğer sen o söz paket hiyerarşi garanti edemez. Yapamıyorsanız - gerçek ihtiyaçlarınız ifade ettiğiniz gibi ise, sadece dizinlere bağlıysa ve paketleme ile herhangi bir gerekli ilişkisi olmadan - o zaman __file__ana dizini bulmak için çalışmanız gerekir (birkaç os.path.dirnameçağrı yapacaktır; - o dizin zaten açık değilse), sonra ( sys.path) başa getirebilir geçici insert çok başlangıcı olarak dir söyledi sys.path, __import__, remove tekrar dir dedi - dağınık işi gerçekten, ama "ne zaman gerekir, yapmalısın" (ve Pyhon rekabetin bu kadar ne yapmaktan programcı durdurmak asla gerekir yapılabilir - sadece ISO C standardında gibi onun önsözünde "Ruh C" bölümünde diyor -!).

İşte sizin için işe yarayabilecek bir örnek:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir

2
bu , aynı modülü farklı adlar ve ilgili tüm hatalar altında kullanılabilir hale getirmek için bir Python paketi içinde bir dizin ekleyebilir sys.path. pypy'deki autopath.py veya bükülmüş içindeki _preamble.py, dizinleri yukarı doğru hareket ettirirken üst düzey paketi tanımlayan bir arama kriteri kullanarak çözer.
jfs

4
İhtiyacınız sys.path.remove(pathYouJustAdded)olan içe aktarmadan sonra bu yeni yolu korumak için değil gibi bir şey yapmak isteyebilirsiniz .
Matthias

35

Modülü, geçerli dizinin tam olarak bir düzey üstünde olan bir dizinden içe aktar:

from .. import module

38
Ben aldım: üst düzey paket ötesinde göreceli ithalat girişiminde bulundu :(
RicardoE

25
kullanarak from .. import module hata aldım ValueError: Öneriyi izleyerek paket dışı göreli içe aktarma girişimi
user3428154 14:18

4

Bir dizin olan bir modül nasıl yüklenir

önsöz: İnsanları python'un ekosisteminde rahatlatmaya yardımcı olmak ve python'un ithalat sistemi ile herkese başarıda en iyi değişikliği vermek umuduyla önceki bir cevabı önemli ölçüde yeniden yazdım.

Bu, OP'nin sorusu için en olası durum olduğunu düşündüğüm bir paket içindeki göreli ithalatı kapsayacaktır .

Python modüler bir sistemdir

Bu nedenle import foo, kök ad alanından yazmak yerine "foo" modülünü yüklemek için yazıyoruz:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python bir dosya sistemine bağlı değil

Bu nedenle Python'u, Jython gibi sanal bir sistem sağlamadan defacto dosya sisteminin olmadığı ortama yerleştirebiliriz.

Bir dosya sisteminden ayrılmak ithalatın esnek olmasını sağlar, bu tasarım arşiv / zip dosyalarından içe aktarma, tek tonları içe aktarma, bayt kodu önbelleğe alma, cffi uzantıları, hatta uzaktan kod tanımı yüklemesi gibi şeylere izin verir.

Peki, içe aktarma bir dosya sistemine bağlı değilse "bir dizin yukarı" ne demektir? Bazı sezgisellerin dışarı almak zorunda ama içinde çalışırken biz örneğin, bunu yapabilir paketin , bazı sezgisel zaten yapar tanımlanmıştır göreli gibi ithalatı .foove..foo aynı paket içinde işi. Güzel!

Kaynak kod yükleme kalıplarınızı bir dosya sistemine içtenlikle birleştirmek istiyorsanız, bunu yapabilirsiniz. Kendi buluşsal yönteminizi seçmeniz ve bir çeşit ithalat makinesi kullanmanız gerekecek , importlib'i tavsiye ederim

Python'un importlib örneği şöyle görünür:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

paketleme

Resmen burada mevcut harika bir örnek proje var: https://github.com/pypa/sampleproject

Python paketi, kaynak kodunuz hakkında, kaynak kodunuzu diğer bilgisayarlara nasıl kopyalayacağınızı ve kaynak kodunuzu bu sistemin yoluna nasıl entegre edeceğinizi bildiren import foo, diğer bilgisayarlarda (yorumlayıcıdan bağımsız olarak, ana bilgisayar işletim sistemi, vb.)

Dizin Yapısı

fooBazı dizinlerde (tercihen boş bir dizin) bir paket adına sahip olalım .

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Benim tercihim setup.pykardeş olarak oluşturmaktır foo.py, çünkü setup.py dosyasını yazmayı kolaylaştırır, ancak isterseniz setuptools'un varsayılan olarak yaptığı her şeyi değiştirmek / yönlendirmek için yapılandırma yazabilirsiniz; örneğin foo.pybir "src /" dizini altına koymak biraz popülerdir, burada ele alınmamaktadır.

some_directory/
    foo.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

"düzenlenebilir" aka -e, kaynak dosyaları bu dizine yüklemek için içe aktarma makinelerini yeniden yönlendirir, bunun yerine geçerli tam dosyaları yükleme ortamının kitaplığına kopyalar. Bu aynı zamanda bir geliştiricinin makinesinde davranışsal farklılıklara neden olabilir, kodunuzu test ettiğinizden emin olun! Pip dışında araçlar var, ancak pip tanıtım biri olmasını tavsiye ederim :)

Ayrıca bir modül (tek bir ".py" dosyası) yerine foobir "paket" (bir dizin içeren __init__.py) yapmak istiyorum , hem "paketler" hem de "modüller" kök ad alanına yüklenebilir, modüller yuvalanmış ad alanlarına izin verir, "göreceli bir dizin yukarı" içe aktarma yapmak istiyorsak faydalıdır.

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

Ben de yapmak istiyorum foo/__main__.py, bu python paketi bir modül olarak yürütmek için izin verir, örneğin olarak python3 -m fooyürütmek .foo/__main__.py__main__

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

Daha fazla modülle bunu halledelim: Temel olarak, aşağıdaki gibi bir dizin yapısına sahip olabilirsiniz:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py geleneksel olarak içindeki kaynak kod hakkında meta veri bilgilerini tutar, örneğin:

  • "install_requires" adlı kurulumu yapmak için hangi bağımlılıklar gereklidir
  • paket yönetimi için hangi ad kullanılmalıdır (yükleme / kaldırma "ad"), bu bizim durumumuzda birincil python paket adınızla eşleştirmenizi öneririm foo , ancak kısa çizgiler için alt çizgi yerine koymak popüler
  • lisans bilgileri
  • vade etiketleri (alfa / beta / vb.),
  • kitle etiketleri (geliştiriciler için, makine öğrenimi için vb.),
  • tek sayfalık dokümantasyon içeriği (README gibi),
  • kabuk adları (bash gibi kullanıcı kabuğuna yazdığınız adlar veya grafik kullanıcı kabuğunda başlat menüsü gibi bulduğunuz adlar),
  • bu paketin yükleneceği (ve kaldırılacağı) python modüllerinin bir listesi
  • defacto "run test" giriş noktası python ./setup.py test

Çok geniş, bir geliştirme makinesine bir kaynak modülü takılıyorsa, c uzantılarını anında derleyebilir. Her gün bir örnek için PYPA Sample Repository's setup.py dosyasını öneriyorum

Bir yapı yapay nesnesi yayınlıyorsanız, örneğin neredeyse aynı bilgisayarları çalıştırmayı amaçlayan kodun bir kopyası varsa, needs.txt dosyası, tam bağımlılık bilgilerinin anlık görüntüsünü almanın popüler bir yoludur; burada "install_requires", minimum ve maksimum uyumlu sürümler. Ancak, hedef makinelerin neredeyse aynı olduğu göz önüne alındığında, tüm bir python önekinin bir tarball oluşturmasını şiddetle tavsiye ederim. Bu zor, buraya girmek için çok ayrıntılı olabilir. pip install'In --targetseçeneğini kontrol edin veya olası satışlar için virtualenv aka venv.

örneğe geri dön

nasıl bir dizin bir dosya almak için:

Foo / spam / eggs.py'den foo / baz'dan kod isteseydik, mutlak ad alanından isteyebiliriz:

import foo.baz

İleride başka bir göreceli bazuygulama ile eggs.py'yi başka bir dizine taşıma özelliğine sahip olmak istiyorsak , aşağıdaki gibi göreli bir içe aktarma kullanabiliriz:

import ..baz

-5

Python modüler bir sistemdir

Python bir dosya sistemine güvenmiyor

Python kodunu güvenilir bir şekilde yüklemek için, bu kodu bir modülde bulundurun ve bu modülün python kütüphanesinde kurulu olmasını sağlayın.

Kurulu modüller her zaman en üst düzey ad alanından yüklenebilir. import <name>


Resmen burada harika bir örnek proje var: https://github.com/pypa/sampleproject

Temel olarak, şöyle bir dizin yapınız olabilir:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

Sizin beyan etmeyi unutmayın setuptools.setup()in setup.py,

resmi örnek: https://github.com/pypa/sampleproject/blob/master/setup.py

Bizim durumumuzda muhtemelen ihracat yapmak istiyoruz bar.pyve foo/__init__.pykısa örneğim:

setup.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

Şimdi modülümüzü python kütüphanesine kurabiliriz; pip ile, the_foo_projectdüzenleme modunda python kitaplığınıza yükleyebilirsiniz , böylece gerçek zamanlı olarak çalışabiliriz

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

Şimdi herhangi bir python bağlamından, paylaşılan py_modules ve paketlerimizi yükleyebiliriz

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))

2
pip install --edit foohemen hemen her zaman bir virtualenv içinde çalışırken modülümü her zaman kurduğumu belirtmeliyim . Neredeyse hiç takılması amaçlanmamış bir modül yazmıyorum. bir şeyi yanlış anlarsam bilmek istiyorum.
ThorSummoner

Ayrıca, editableyumurta bağlantıları ve kurulu python modülleri her şekilde tam olarak aynı olmadığından , toks gibi bir venv-otomatik oluşturma test paketi kullanmanın çok yararlı olduğunu da belirtmeliyim ; örneğin, düzenlenebilir bir modüle yeni bir ad alanı eklemek yol aramanızda bulunur, ancak bu setup.py dosyanızda dışa aktarılmazsa paketlenmez / kurulmaz! Kullanım durumunuzu test edin :)
ThorSummoner
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.