Farklı klasördeki dosyaları içe aktarma


1406

Aşağıdaki klasör yapısına sahibim.

application/app/folder/file.py

ve içinde bulunan başka bir Python dosyasında file.py'den bazı işlevleri almak istiyorum

application/app2/some_folder/some_file.py

denedim

from application.app.folder.file import func_name

ve başka çeşitli girişimler yaptım ama şimdiye kadar düzgün bir şekilde içe aktarmayı başaramadım. Bunu nasıl yapabilirim?



Resmi belgeleri okumak bana çok yardımcı oldu! docs.python.org/3/reference/…
Kavin Raju S

Yanıtlar:


1441

Varsayılan olarak yapamazsınız. Bir dosyayı içe aktarırken, Python yalnızca geçerli dizinde, giriş noktası komut dosyasının çalıştığı sys.pathdizinde ve paket yükleme dizini gibi konumları içerir (aslında bundan biraz daha karmaşıktır, ancak bu çoğu vakayı kapsar) .

Ancak, çalışma zamanında Python yoluna ekleyebilirsiniz:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file

355
sys.path.append('/path/to/application/app/folder')imo
pseudosudo

387
@pseudosudo: Evet, ancak başlangıçta eklemek, çakışma adlandırmalarında yolun diğerlerinden önce (yerleşik olanlar bile) aranmasını garanti etme yararına sahiptir.
Cameron

8
@kreativitea - sys.pathBir döner listdeğil, bir dequeve dönüştürmek saçma olurdu listbir etmek dequeve arka.
ArtOfWarfare

37
Klasörlerdeki .py dosyalarını yönetmenin pythonic bir yolu olarak mı görülüyor? Merak ediyorum ... neden varsayılan olarak desteklenmiyor? tüm .py dosyalarını tek bir dizinde tutmak mantıklı değil ..
Ofir

49
@Ofir: Hayır, bu hoş ve temiz bir pitonik çözüm değil. Genel olarak, (dizin ağaçlarını temel alan) paketleri kullanmalısınız. Bu cevap, sorulan soruya özgüdür ve bazı nedenlerden dolayı çok sayıda oy toplamaya devam etmektedir.
Cameron

709

Şununla ilgili yanlış bir şey yok:

from application.app.folder.file import func_name

Sadece bir folderde içerdiğinden emin olun __init__.py, bu bir paket olarak dahil edilmesini sağlar. Diğer cevapların neden bahsettiğinden emin değilim PYTHONPATH.


47
Çünkü bu modifikasyonun PYTHONPATHgerekli olduğu durumları kapsamaz . Aynı düzeyde iki klasörünüz olduğunu varsayalım: Ave B. Avardır __init.py__. Bir şey ithal deneyin Biçinde A.
msvalkon

32
İçinde ne var init.pyya __init__.pydosyanın?
Lorem Ipsum Dolor

47
@Xinyang Boş bir dosya olabilir. Varlığı Python'a dizine bir paket gibi davranmasını söyler.
jay

9
Tabii ki bu cevap varsayar applicationve apppaketler (var zaten yani zaten __init__.pyher ikisinde de). Eklenmesinin sonucu olarak __init__.pyda folder, application.app.folderbir (alt) olur paketini sen erişebileceği, modül application.app.folder.file sembol, func_nameşimdi alınabilir
Yibo Yang

30
Her ne denersem, bu işe yaramaz. Bir "kardeş" dizinden, bir yukarı bir aşağı almak istiyorum. Hepsinde ebeveyn dahil __ init __. Py var. Bu python 3'e özgü mü?
dasWesen

109

Modüller, sorudaki gibi paralel konumlarda olduğunda:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Bu stenografi bir modülü diğerine görünür kılar:

import sys
sys.path.append('../')

24
Bir uyarı olarak: Bu, içe aktarılan komut dosyası içeren dizinden çalıştırıldığı sürece çalışır. Aksi takdirde, komut dosyasının çalıştırıldığı diğer dizinlerin üst dizini yola eklenir ve içe aktarma başarısız olur.
Carl Smith

14
Bundan kaçınmak için dosyanın ana dizinini alabilirizsys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul

Bu benim için işe yaramadıcli/foo.pyimport cli.bar
Ebeveyne

2
@Rahul, çözümünüz etkileşimli kabuklar için çalışmıyor
towi_parallelism

Kök klasörünüzden (yani uygulama klasörü) çalıştırırsanız, sys.path.append('.')modülü kullanarak içe aktarma konusunda sorun yoktur from app2.some_folder.some_file import your_function. Alternatif olarak benim için çalışan python3 -m app2.another_folder.another_filekök klasörden çalışıyor .
bağımlısı

68

Name-file.py dosyasındaki ilk içe aktarma sistemleri

 import sys

İkincisi , name-file.py dosyasına klasör yolunu ekleyin

sys.path.insert(0, '/the/folder/path/name-package/')

Üçüncü Alt dizinde __ init __.py adında boş bir dosya oluşturun (bu, Python'a bir paket olduğunu söyler)

  • name-file.py
  • ad-paket
    • __ init __.py
    • name-module.py

Dördüncüsü , name-file.py klasöründeki modülü içe aktarın

from name-package import name-module

5
Ad klasörü name-file.py dosyasının hemen altında olduğunda, bu sys.path.insert-command olmadan da çalışmalıdır. Bu nedenle, ad klasörü rasgele bir konumda olsa bile bu çözüm çalışırsa, cevap sorudan ayrılır.
Bastian

55

Geçici bir yol , belgede açıklandığı gibi ortam değişkeniniPYTHONPATH kullanmak olacağını düşünüyorum : Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%

Bekle, myScripts'i dosya adıyla değiştirir miyim?
Vladimir Putin

4
no, dizinin .py dosyanızın yolu ile
Ax3l

1
Ne yazık ki, Anaconda kullanıyorsanız, bu işe yaramaz, çünkü PYTHONPATH başlık altında gerçekten dahili olarak kullanılmaz!
information_interchange

Anaconda'daki (son) değişiklikler için, iş akışları ve geçici çözümlere ilişkin yorumlar için bu SO'ya bakın: stackoverflow.com/questions/17386880/… Genel olarak, içe aktarma dizinlerini kesmek yerine küçük paketler oluşturun ve kurun.
Ax3l

46

Burada cevaplar net değil, bu Python 3.6 üzerinde test edildi

Bu klasör yapısıyla:

main.py
|
---- myfolder/myfile.py

myfile.pyİçerik nerede :

def myfunc():
    print('hello')

İçindeki içe aktarma ifadesi main.py:

from myfolder.myfile import myfunc
myfunc()

ve bu merhaba yazdıracaktır .


9
klasörümde bir init .py (boş) yapılandırma dosyası ekleyerek linux (y) üzerinde çalıştı
Vincent

7
@ Bunu mu demek istediniz __init__.py?
mrgloom

2
@mrgloom indeed
Vincent

3
Nedense ekleme __init__.pybenim için işe yaramıyor. Ubuntu 18'de Py 3.6.5 kullanıyorum. Pycharm üzerinde çalışıyor ancak terminalden değil
Crearo Rotar 15:18

9
Bu, geçerli çalışma dizininden farklı bir dosya ağacından dosyaları içe aktarma hakkında soru soran soru ile tamamen ilgisizdir.
Alexander Rossa

44

Sorununuz Python'un Python dizininde bu dosyayı araması ve bulamamasıdır. Python dizini değil, içinde bulunduğunuz dizin hakkında konuştuğunuzu belirtmelisiniz.

Bunu yapmak için şunu değiştirirsiniz:

from application.app.folder.file import func_name

buna:

from .application.app.folder.file import func_name

Nokta ekleyerek, Python dizinine bakmak yerine uygulama klasörü için bu klasöre bakın diyorsunuz.


2
Beni bu düzeltti
Phi

32

Bildiklerimden, __init__.pydoğrudan içe aktarmak istediğiniz işlevlerin klasörüne bir dosya eklemek işi yapacak.


7
yalnızca bu dizini eklemek isteyen komut dosyası zaten sys.path
dosyasındaysa

2
sys.path.append(tools_dir)Windows'ta kullandım ve bir __init__.py' file in my directory tools_dir` eklememe gerek yok
herve-guerin

25

Python 3.4 ve sonraki sürümlerinde, doğrudan bir kaynak dosyadan içe aktarabilirsiniz (belgelere bağlantı) . Bu en basit çözüm değil, ama bu cevabı bütünlük için ekliyorum.

İşte bir örnek. İlk olarak, içe aktarılacak dosya foo.py:

def announce():
    print("Imported!")

Yukarıdaki dosyayı içe aktaran kod, dokümantasyondaki örnekten büyük ölçüde ilham aldı:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

Çıktı:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Değişken adı, modül adı ve dosya adının eşleşmesi gerekmediğini unutmayın. Bu kod hala çalışıyor:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

Çıktı:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Programlı olarak içe aktarılan modüller Python 3.1'de tanıtıldı ve modüllerin nasıl içe aktarıldığı üzerinde daha fazla kontrol sahibi olmanızı sağlar. Daha fazla bilgi için belgelere bakın.


11
Kimsenin bunu anlamaya çalışıp çalışmadığını bilmiyorum, ama bence çok karmaşık.
Dan

23

Çalıştı benim için içinde python3 üzerinde linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  

6
sys.path.append("/home/linux/folder/")- Bir kısayol kullanmadığınızdan emin olun örn."~/folder/"
daGo

22

Python'un göreli ithalatlarını deneyin:

from ...app.folder.file import func_name

Baştaki her nokta, geçerli dizinden başlayarak hiyerarşideki bir başka üst düzeydir.


Sorunlar? Bu sizin için işe yaramıyorsa, muhtemelen birçok gotcha'nın göreceli ithalatına göre biraz alıyorsunuz. Daha fazla ayrıntı için yanıtları ve yorumları okuyun: __init__.py ile bile "Paket dışı göreli içe aktarma denemesi" nasıl düzeltilir?

İpucu: __init__.pyher dizin düzeyinde. En python -m application.app2.some_folder.some_fileüst düzey dizinden çalıştırdığınız veya PYTHONPATH'nizde bu üst düzey dizine sahip olmanız (.py dışında bırakarak) gerekebilir . Uf!


22

Aynı meydan okuma ile karşı karşıya kaldım, özellikle birden fazla dosyayı içe aktarırken, bu şekilde üstesinden gelmeyi başardım.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name

2
Sıradan bir ithalattan farklı olarak ne yaptığını açıklayabilirseniz, yanıtınız daha yararlı olur mu?
not2qubit

1
/Path/dir1/__init__.py ve /path/dir1/mod.py dosyalarım vardı. Dir1.mod'dan /path/some.py için import func çalıştı. /Path/dir2/some.py dosyasında yalnızca yukarıdaki kopyayı dosyanın üst kısmına kopyalayıp yapıştırdıktan sonra çalıştı. Sahip olduğum her python projesi / path / içinde olmadığından yolumu düzenlemek istemedim.
jwal

19

Düşünüldüğünde applicationsizin piton proje için kök dizin olarak, boş bir oluşturmak __init__.pydosyayı application, appve folderklasörleri. Sonra some_file.pyfunc_name tanımını almak için değişikliklerinizi aşağıdaki gibi yapın:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()

olması gereken: sys.path.insert (0, r '/ from / root / directory')
Bolaka

18

Uygulamayı diğer ortamlara taşırken sys.path.append komutunu mutlak bir yolla kullanmak ideal değildir. Geçerli çalışma dizini komut dosyasının nasıl çağrıldığına bağlı olduğundan, göreli bir yol kullanmak her zaman çalışmaz.

Uygulama klasörü yapısı düzeltildiğinden, içe aktarmak istediğimiz modülün tam yolunu almak için os.path komutunu kullanabiliriz. Örneğin, eğer yapı bu ise:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

Ve diyelim ki "mango" modülünü içe aktarmak istiyorsunuz. Vanilla.py ile aşağıdakileri yapabilirsiniz:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Tabii ki, mango_dir değişkenine ihtiyacınız yok.

Bunun nasıl çalıştığını bu etkileşimli oturum örneğine nasıl bakmak için:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

Ve os.path belgelerine bakın.


13

Bu benim için pencerelerde çalışıyor

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file

10

Oldukça özelim: Windows ile Python kullanıyorum!

Sadece bilgileri tamamlıyorum: hem Windows hem de Linux için hem göreli hem de mutlak yol çalışır sys.path(Göreli yollara ihtiyacım var çünkü scriptlerimi birkaç bilgisayarda ve farklı ana dizinlerde kullanıyorum).

Ve Windows'u hem kullanırken \ve /dosya adları için ayırıcı olarak kullanılabilir ve tabii ki iki katına gerekir \Python dizeleri, içine
bazı geçerli örnekler:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(not: Bu , Linux uyumlu ve Windows explorer'a yazmak ve kopyalamak daha basit olduğu için daha az 'Windows yerel' ise olaydan /daha uygun olduğunu düşünüyorum \)


4
os.path.join ('araçlar', 'mydir')
Corey Goldberg

7

Belirli bir yoldan bir modül yüklemenin amacı, özel bir modülün geliştirilmesi sırasında size yardımcı olmaksa, test komut dosyasının aynı klasöründe özel modülün köküne işaret eden sembolik bir bağlantı oluşturabilirsiniz. Bu modül başvurusu, o klasörde çalıştırılan herhangi bir komut dosyası için aynı adda yüklenen diğer tüm modüllere göre önceliklidir.

Bunu Linux'ta test ettim, ancak sembolik bağlantıları destekleyen herhangi bir modern işletim sisteminde çalışmalıdır.

Bu yaklaşımın bir avantajı, kendi yerel SVC şube çalışma kopyanızda oturan ve geliştirme döngüsü süresini büyük ölçüde basitleştirebilen ve modülün farklı sürümlerini yönetmede hata modlarını azaltabilen bir modülü işaret edebilmenizdir.


7

Benim durumumda ithal edecek bir dersim vardı. Dosyam şöyle görünüyordu:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

Ana dosyama yoluyla kodu dahil ettim:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper

1
@ not2qubit sys yanıtta içe aktarılmadı.
Walter

7

Aynı soruya birkaç kez çarptım, bu yüzden çözümümü paylaşmak istiyorum.

Python Sürümü: 3.X

Aşağıdaki çözüm, Python 2 Ocak / 1 / 2020'den beri desteklenmediği için uygulamanızı Python sürüm 3.X'te geliştiren kişiler içindir .

Proje Yapısı

Python 3'te Örtülü Ad Alanı Paketleri__init__.py nedeniyle proje alt dizininize ihtiyacınız yoktur . Bkz Is init .py Python paketleri için gerekli değildir 3.3+

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Sorun bildirimi

Gelen file_b.py, bir sınıfını içe istiyorum Aiçindefile_a.py klasör a altında.

Çözümler

# 1 Hızlı ama kirli bir yol

Paketi sizin gibi yüklemeden şu anda yeni bir proje geliştiriyorsunuz

Kullanma try catchhataları kontrol etmek. Kod örneği:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Paketinizi kurun

Uygulamanızı yükledikten sonra (bu yayında, yükleme eğitimi dahil değildir)

Basitçe yapabilirsiniz

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Mutlu kodlama!



3

aKullanıcıların pip install aaşağıdaki dosya listesi aracılığıyla yüklemelerini istediğim bir proje üzerinde çalışıyordum :

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

A / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

A / B / b.py

dogs = 1

Modülü aşağıdaki dizinden çalıştırarak yükledim MANIFEST.in:

python setup.py install

Sonra, dosya sistemimdeki tamamen farklı bir konumdan /moustache/armwrestleçalıştırabildim:

import a
dir(a)

Bu da , amaçlandığı gibi, gerçekten a.cats0 ve a.b.dogsgerçekten de 1'e eşit olduğunu doğruladı .


2

Sadece bir yapmak yerine, şunu yapın import ...:

from <MySubFolder> import <MyFile>

MyFile, MySubFolder içinde.


1

Python kabuğunu f5 tuşuna basarak yenileyebilir veya Çalıştır-> Çalıştır Modülü'ne gidebilirsiniz. Bu şekilde dosyadan bir şey okumak için dizini değiştirmeniz gerekmez. Python dizini otomatik olarak değiştirir. Ancak, Python Kabuğu'ndaki farklı dizinden farklı dosyalarla çalışmak istiyorsanız, Cameron'un daha önce söylediği gibi sys'deki dizini değiştirebilirsiniz .


1

Bu yüzden IDE'mi sağ tıkladım ve yeni bir tane ekledim folderve neden ondan alamadığımı merak ediyordum. Daha sonra klasik bir dosya sistemi klasörü yerine sağ tıklayıp bir Python Paketi oluşturmam gerektiğini fark ettim. Ya da __init__.pydiğer yanıtlarda belirtildiği gibi bir post-mortem yöntemi (python'un dosya sistemi klasörünü bir paket olarak ele almasını sağlar) ekleniyor . Birisinin bu rotaya gitmesi durumunda bu cevabı buraya ekliyoruz.


1

Aşağıdaki gibi bir dize kullanarak bir klasörden bir modülü içe aktarmak istediğiniz modülleri almak için importlib komutunu kullanabilirsiniz:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

Bu örnekte, yukarıdaki kod olan bir main.py, ardından Scripts adlı bir klasör vardır ve daha sonra scriptNamedeğişkeni değiştirerek ihtiyacınız olan her şeyi bu klasörden çağırabilirsiniz . Daha sonra scriptbu modüle başvurmak için kullanabilirsiniz . Hello()Yılan modülünde denilen bir işlev varsa, bunu yaparak bu işlevi çalıştırabilirsiniz gibi:

script.Hello()

Bunu Python 3.6'da test ettim


0

Bu sorunları birkaç kez yaşadım. Aynı sayfaya çok geldim. Son sorunumda serversabit bir dizinden çalıştırmak zorunda kaldım , ancak her hata ayıklamada farklı alt dizinlerden çalıştırmak istedim.

import sys
sys.insert(1, /path) 

did DEĞİL nedeniyle farklı modüllerin benim için çalışmalarını farklı okumak zorunda * .csv aynı dizinde hepsi dosyaları.

Sonunda, benim için işe yarayan şey pitonik değildi, sanırım, ama:

Hata ayıklamak istediğim modülün üstünde birif __main__ olağan yoldan daha farklı bir yerden çalıştırıldığı.

Yani:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
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.