it-swarm-tr.com

Python'da göreli ithalat nasıl yapılır?

Bu dizin yapısını düşünün:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

mod1 kodluyorum ve mod2 adresinden bir şey almam gerekiyor. Bunu nasıl yapmalıyım?

from ..sub2 import mod2 dosyasını denedim, ancak "paket dışı göreceli içe aktarma girişimi" alıyorum.

Etrafta googledim ama sadece "sys.path manipülasyon" hackleri buldum. Temiz bir yolu yok mu?


Düzenleme: tüm __init__.py 'ler şu anda boş

Düzen2: Bunu yapmaya çalışıyorum çünkü alt2 alt paketler arasında paylaşılan sınıfları içeriyor (sub1, subX, vb.).

Edit3: Aradığım davranış PEP 366 (teşekkürler John B) 'de açıklandığı gibi

487
Joril

Herkes sadece soruyu cevaplamak yerine ne yapmanız gerektiğini söylemek istiyor gibi görünüyor.

Sorun mod1.py'yi yorumlayıcıya argüman olarak ileterek modülü '__main__' olarak çalıştırıyor olmanızdır.

PEP 328 :

Göreceli içe aktarma, bu modülün paket sıradüzenindeki konumunu belirlemek için bir modülün __niteliğini kullanır. Modülün adı herhangi bir paket bilgisi içermiyorsa (örneğin, '__main__' olarak ayarlanmışsa), göreli ithalat, modülün gerçekte dosya sisteminde nerede bulunduğuna bakılmaksızın, üst düzey bir modülmiş gibi çözümlenir.

Python 2.6'da ana modüle göre modüllere referans verme yeteneği ekliyorlar. PEP 366 değişikliği açıklar.

Güncelleme : Nick Coghlan'a göre önerilen alternatif, -m anahtarını kullanarak modülü paketin içinde çalıştırmaktır.

306
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. python main.py dosyasını çalıştırıyorsunuz.
  2. main.py mu: import app.package_a.module_a
  3. module_a.py mu import app.package_b.module_b

Alternatif olarak 2 veya 3 şunları kullanabilir: from app.package_a import module_a

Bu, PYTHONPATH'inizde app bilgisine sahip olduğunuz sürece çalışacaktır. main.py, herhangi bir yerde olabilirdi.

Böylece tüm uygulama paketini ve alt paketlerini hedef sistemin python klasörlerine kopyalamak (yüklemek) ve hedefin komut dosyası klasörlerine setup.py yazmak için bir main.py yazarsınız.

116
nosklo

İşte benim için işe yarayan çözüm:

Göreceli ithalatları from ..sub2 import mod2 olarak yapıyorum ve sonra, mod1.py komutunu çalıştırmak istersem app üst dizinine gidip python -m anahtarını python -m app.sub1.mod1 kullanarak kullanarak modülü çalıştırıyorum.

Bu sorunun göreceli ithalatla ortaya çıkmasının asıl nedeni, göreceli ithalatın modülün __name__ özelliğini alarak çalışmasıdır. Modül doğrudan çalıştırılıyorsa, __name____main__ olarak ayarlanır ve paket yapısı hakkında herhangi bir bilgi içermez. Ve bu yüzden python relative import in non-package hatası hakkında şikayet ediyor.

Böylece, -m anahtarını kullanarak, göreli ithalatı başarılı bir şekilde çözebilecekleri python'a paket yapı bilgilerini verirsiniz.

Göreceli ithalat yaparken bu sorunla pek çok kez karşılaştım. Ve önceki tüm cevapları okuduktan sonra, hala tüm dosyalara boyler kodunu koymaya gerek kalmadan, temiz bir şekilde nasıl çözeceğimi çözemedim. (Yorumların bazıları gerçekten yardımcı olsa da, @ncoghlan ve @XiongChiamiov sayesinde)

Umarım bu göreceli ithalat sorunuyla mücadele eden birine yardım eder, çünkü PEP’den geçmek gerçekten eğlenceli değil.

114
Pankaj

"Guido bir paket içinde çalışan komut dosyalarını bir kalıp karşıtı olarak görüyor" (reddedildi PEP-3122 )

Bir çözüm bulmaya çalışmak için çok zaman harcadım, Stack Overflow'taki ilgili yazıları burada okudum ve kendime “daha ​​iyi bir yol olmalı!” Demiştim. Olmamış gibi görünüyor.

46
lesnik

Bu% 100 çözüldü:

  • uygulama /
    • main.py
  • ayarlar /
    • local_setings.py

/ Local.setting.py ayarlarını app/main.py içine alın:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

Bu kod parçasını yollardan modülleri almak için kullanıyorum, yardımcı olacağını umuyorum

24
iElectric

nosklo's cevabının örneklerle açıklanması

not: tüm __init__.py dosyaları boş.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

uygulama/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

uygulama/package_b/fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

$ python main.py komutunu çalıştırırsanız şunu verir:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py yapar: from app.package_b import fun_b
  • fun_b.py yok from app.package_a.fun_a import print_a

yani package_b klasöründeki dosya, package_a klasöründe kullanılan dosya. Sağ??

21
suhailvs

Bu ne yazık ki bir sys.path kesmek, ama oldukça iyi çalışıyor.

Bu sorunla başka bir katmanla karşılaştım: Belirtilen adda bir modül zaten vardı, ama yanlış modüldü.

yapmak istediğim şuydu: (Çalıştığım modül modül3 idi):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

Unutmayın ki mymodule, ancak kurulumumda "mymodule1" yok.

ve yüklü bir modülden içe aktarmaya çalıştığım için bir ImportError alırdım.

Bir sys.path.append yapmaya çalıştım ve bu işe yaramadı. Ne işe yaradı sys.path.insert

if __== '__main__':
    sys.path.insert(0, '../..')

Yani bir tür kesmek, ama hepsi işe yaradı! Bu yüzden, kararınızı diğer yolları geçersiz kıl istiyorsanız, çalışmak için sys.path.insert (0, yol adı) kullanmanız gerekiyorsa, aklınızda bulundurun! Bu benim için çok sinir bozucu bir noktaydı, insanların birçoğu sys.path için "append" işlevini kullandığını söylüyor, ama zaten tanımlanmış bir modülünüz varsa işe yaramaz (ben bunu çok garip davranışlar bulurum)

11
Garrett Berg

Bunu buraya kendi referansım için koyalım. İyi bir Python kodu olmadığını biliyorum, ancak üzerinde çalıştığım bir proje için bir komut dosyasına ihtiyacım vardı ve bu komut dosyasını scripts dizinine koymak istedim.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
10
milkypostman

@EvgeniSergeev'in OP’e yaptığı açıklamalarda dediği gibi, bir .py dosyasından isteğe bağlı bir konumda kod alabilirsiniz:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Bu, bu SO cevabından alınmıştır.

8
LondonRob
4
mossplix

Python doc

Python 2.5'de, bir from __future__ import absolute_import yönergesini kullanarak içe aktarma davranışını mutlak içe aktarma işlemine geçirebilirsiniz. Bu mutlak alma davranışı gelecekteki bir sürümde varsayılan olur (muhtemelen Python 2.7). Mutlak ithalatlar varsayılan olduğunda, import string her zaman standart kütüphanenin sürümünü bulacaktır. Kullanıcıların mümkün olduğu kadar mutlak ithalat kullanmaya başlaması önerilmektedir, bu nedenle kodunuza from pkg import string yazmaya başlamanız tercih edilir.

2
jung rhew

John B'nin söylediğinin üstüne, başka şeyleri mahvedebilecek __package__ yerine __main__ değişkeninin ayarlanması yardımcı olacak gibi görünüyor. Ancak test edebildiğim kadarıyla tamamen olması gerektiği gibi çalışmaz.

Aynı problemi yaşıyorum ve ne PEP 328 ne de 366 problemi tam olarak çözmedi, çünkü hem günün sonunda hem de anlayabildiğim kadarıyla sys.path içine dahil edilecek paketin başına ihtiyacım var.

Ayrıca, bu değişkenlere girmesi gereken dizeyi nasıl biçimlendireceğimi bulamadığımı da belirtmeliyim. "package_head.subfolder.module_name" mı yoksa ne?

1
Gabriel

"PYTHONPATH" ortam değişkenini üst klasöre ayarlamanın daha kolay olduğunu gördüm:

bash$ export PYTHONPATH=/PATH/TO/APP

sonra:

import sub1.func1
#...more import

tabii ki, PYTHONPATH "küresel", ama henüz benim için sorun yaratmadı.

1
Andrew_1510