Nhập tệp từ thư mục khác nhau


1407

Tôi có cấu trúc thư mục sau.

application/app/folder/file.py

và tôi muốn nhập một số chức năng từ file.py trong một tệp Python khác nằm trong

application/app2/some_folder/some_file.py

Tôi đã thử

from application.app.folder.file import func_name

và một số nỗ lực khác nhau nhưng cho đến nay tôi không thể quản lý để nhập đúng. Tôi có thể làm cái này như thế nào?



Đọc tài liệu chính thức đã giúp tôi rất nhiều! docs.python.org/3/reference/ trộm
Kavin Raju S

Câu trả lời:


1444

Theo mặc định, bạn không thể. Khi nhập tệp, Python chỉ tìm kiếm thư mục hiện tại, thư mục mà tập lệnh điểm nhập đang chạy và sys.pathbao gồm các vị trí như thư mục cài đặt gói (thực tế nó phức tạp hơn một chút, nhưng điều này bao gồm hầu hết các trường hợp) .

Tuy nhiên, bạn có thể thêm vào đường dẫn Python khi chạy:

# 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

353
sys.path.append('/path/to/application/app/folder')là sạch hơn imo
pseudosudo

387
@pseudosudo: Đúng, nhưng việc chèn nó vào đầu có lợi ích đảm bảo rằng đường dẫn được tìm kiếm trước những người khác (thậm chí là tích hợp sẵn) trong trường hợp đặt tên xung đột.
Cameron

8
@kreativitea - sys.pathtrả về a list, không phải a dequevà thật ngớ ngẩn khi chuyển đổi listthành a dequevà trở lại.
ArtOfWarfare

37
Có được coi là một cách pythonic để quản lý các tệp .py trong các thư mục không? Tôi đang tự hỏi ... tại sao nó không được hỗ trợ theo mặc định? Sẽ không có ý nghĩa gì khi duy trì tất cả các tệp .py trong một thư mục duy nhất ..
Ofir

49
@Ofir: Không, đây không phải là một giải pháp pythonic sạch đẹp. Nói chung, bạn nên sử dụng các gói (dựa trên cây thư mục). Câu trả lời này là cụ thể cho câu hỏi được hỏi, và vì một số lý do tiếp tục tích lũy một số lượng lớn upvote.
Cameron

709

Không có gì sai với:

from application.app.folder.file import func_name

Chỉ cần chắc chắn rằng foldercũng chứa một __init__.py, điều này cho phép nó được bao gồm như một gói. Không chắc chắn tại sao các câu trả lời khác nói về PYTHONPATH.


47
Bởi vì điều này không bao gồm các trường hợp cần sửa đổi PYTHONPATH. Giả sử bạn có hai thư mục ở cùng cấp độ: AB. Acó một __init.py__. Hãy thử nhập một cái gì đó từ Bbên trong A.
msvalkon

32
Có gì bên trong init.pyhoặc __init__.pytập tin?
Lorem Ipsum Dolor

48
@Xinyang Nó có thể là một tập tin trống. Chính sự tồn tại của nó bảo Python coi thư mục là một gói.
jay

9
Tất nhiên câu trả lời này giả định rằng applicationapplà các gói đã có (tức là bạn đã có __init__.pytrong cả hai). Kết quả của việc thêm __init__.pyvào folder, application.app.foldertrở thành gói (phụ) , từ đó bạn có thể truy cập mô-đun application.app.folder.file , biểu tượng func_namehiện có thể được nhập
Yibo Yang

30
Dù tôi cố gắng, điều này sẽ không hoạt động. Tôi muốn nhập từ một thư mục "anh chị em", vì vậy một trong một xuống. Tất cả đều có __ init __. Của tôi, bao gồm cả cha mẹ. Đây có phải là con trăn 3 không?
dasWesen

111

Khi các mô-đun ở vị trí song song, như trong câu hỏi:

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

Tốc ký này làm cho một mô-đun hiển thị cho mô-đun khác:

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

24
Như một lời cảnh báo: Điều này hoạt động miễn là tập lệnh nhập được chạy từ thư mục chứa nó. Mặt khác, thư mục mẹ của bất kỳ thư mục nào khác mà tập lệnh được chạy từ đó sẽ được thêm vào đường dẫn và quá trình nhập sẽ thất bại.
Carl Smith

14
Để tránh điều đó, chúng ta có thể lấy thư mục mẹ của tệpsys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul

Điều đó không hiệu quả với tôi - tôi đã phải thêm một dirname bổ sung vào đó để trèo ngược lên cha mẹ, để chạy cli/foo.pytừ dòng lệnh có thểimport cli.bar
RCross 26/07/19

2
@Rahul, giải pháp của bạn không hoạt động đối với hệ vỏ tương tác
Towi_metism

Nếu bạn chạy nó từ thư mục gốc (ví dụ: thư mục ứng dụng), có lẽ bạn sẽ ổn sys.path.append('.')khi nhập mô-đun bằng cách sử dụng from app2.some_folder.some_file import your_function. Ngoài ra, những gì làm việc cho tôi là chạy python3 -m app2.another_folder.another_filetừ thư mục gốc.
nghiện

68

Sys nhập đầu tiên trong name-file.py

 import sys

Thứ hai nối đường dẫn thư mục trong name-file.py

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

Thứ ba Tạo một tệp trống có tên __ init __.py trong thư mục con của bạn (điều này cho Python biết đó là một gói)

  • tên-fileile
  • tên gói
    • __ init __.py
    • tên-mô-đun

Thứ tư nhập mô-đun bên trong thư mục trong name-file.py

from name-package import name-module

5
Với thư mục tên nằm ngay bên dưới name-file.py, thư mục này sẽ hoạt động ngay cả khi không có sys.path.insert-command. Như vậy, câu trả lời để lại câu hỏi, nếu giải pháp này hoạt động ngay cả khi thư mục tên được đặt ở một vị trí tùy ý.
Bastian

55

Tôi nghĩ rằng một cách đặc biệt sẽ là sử dụng biến môi trườngPYTHONPATH như được mô tả trong tài liệu: Python2 , Python3

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

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

Đợi đã, tôi sẽ thay thế myScripts bằng tên tệp chứ?
Vladimir Putin

4
không, với đường dẫn của thư mục đến tệp .py của bạn
Ax3l

1
Thật không may, nếu bạn đang sử dụng Anaconda, điều này sẽ không hoạt động, vì dưới mui xe PYTHONPATH không thực sự được sử dụng trong nội bộ!
information_interchange

Để biết những thay đổi (gần đây) trong anaconda, hãy xem SO này để biết quy trình công việc và nhận xét cho công việc: stackoverflow.com/questions/17386880/ Nói chung, xây dựng và cài đặt các gói nhỏ thay vì hack các thư mục nhập khẩu.
Ax3l

47

Các câu trả lời ở đây thiếu rõ ràng, điều này đã được thử nghiệm trên Python 3.6

Với cấu trúc thư mục này:

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

Trường hợp myfile.pycó nội dung:

def myfunc():
    print('hello')

Báo cáo nhập khẩu trong main.pylà:

from myfolder.myfile import myfunc
myfunc()

và điều này sẽ in xin chào .


9
thêm tệp cấu hình init .py (trống) trong myfolder đã hoạt động với tôi trên linux (y)
Vincent

7
@ Ý bạn là __init__.pygì?
mrgloom

2
@mrgloom thực sự
Vincent

3
Đối với một số lý do thêm __init__.pykhông làm việc cho tôi. Tôi đang sử dụng Py 3.6.5 trên Ubuntu 18. Nó hoạt động trên Pycharm nhưng không phải từ thiết bị đầu cuối
Crearo Rotar

9
Điều này hoàn toàn không liên quan đến câu hỏi hỏi về việc nhập tệp từ một nhánh khác của cây tệp so với thư mục làm việc hiện tại.
Alexander Rossa

45

Vấn đề của bạn là Python đang tìm trong thư mục Python cho tệp này và không tìm thấy nó. Bạn phải xác định rằng bạn đang nói về thư mục mà bạn đang ở chứ không phải Python.

Để làm điều này, bạn thay đổi điều này:

from application.app.folder.file import func_name

đến đây:

from .application.app.folder.file import func_name

Bằng cách thêm dấu chấm bạn đang nói hãy tìm trong thư mục này cho thư mục ứng dụng thay vì tìm trong thư mục Python.


2
đây là điều đã cố định tôi
Phi

32

Từ những gì tôi biết, thêm một __init__.pytệp trực tiếp vào thư mục của các chức năng bạn muốn nhập sẽ thực hiện công việc.


7
chỉ khi tập lệnh muốn bao gồm thư mục khác đó đã có trong sys.path
Ax3l

2
Tôi đã sử dụng sys.path.append(tools_dir)trên Windows và tôi không cần thêm __init__.py' file in my directory tools_dir`
herve-guerin 18/03/2017

25

Trong Python 3.4 trở lên, bạn có thể nhập trực tiếp từ tệp nguồn (liên kết đến tài liệu) . Đây không phải là giải pháp đơn giản nhất, nhưng tôi bao gồm câu trả lời này cho đầy đủ.

Đây là một ví dụ. Đầu tiên, tập tin được nhập, có tên foo.py:

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

Mã nhập tệp ở trên, lấy cảm hứng rất nhiều từ ví dụ trong tài liệu:

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()

Đầu ra:

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

Lưu ý rằng tên biến, tên mô-đun và tên tệp không cần phải khớp. Mã này vẫn hoạt động:

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()

Đầu ra:

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

Các mô-đun nhập theo lập trình đã được giới thiệu trong Python 3.1 và cung cấp cho bạn nhiều quyền kiểm soát hơn về cách các mô-đun được nhập. Tham khảo tài liệu để biết thêm thông tin.


11
Tôi không biết có ai từng cố gắng hiểu điều này không, nhưng tôi nghĩ rằng nó quá phức tạp.
Dan

23

Làm việc cho tôi trong python3 trên linux

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

6
sys.path.append("/home/linux/folder/")- Đảm bảo không sử dụng một phím tắt ví dụ"~/folder/"
Dago

22

Hãy thử nhập khẩu tương đối của Python:

from ...app.folder.file import func_name

Mỗi dấu chấm hàng đầu là một cấp độ cao hơn khác trong hệ thống phân cấp bắt đầu với thư mục hiện tại.


Các vấn đề? Nếu điều này không hiệu quả với bạn thì có lẽ bạn đang nhận được một chút bởi nhiều lần nhập tương đối của gotcha. Đọc câu trả lời và nhận xét để biết thêm chi tiết: Cách khắc phục "Đã cố nhập tương đối trong gói không" ngay cả với __init__.py

Gợi ý: có __init__.pyở mọi cấp độ thư mục. Bạn có thể cần python -m application.app2.some_folder.some_file(bỏ đi .py) mà bạn chạy từ thư mục cấp cao nhất hoặc có thư mục cấp cao nhất đó trong PYTHONPATH của bạn. Phù!


22

Tôi đã phải đối mặt với cùng một thách thức, đặc biệt là khi nhập nhiều tệp, đây là cách tôi quản lý để vượt qua nó.

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
Bạn trả lời sẽ hữu ích hơn nếu bạn có thể giải thích những gì nó làm khác với nhập khẩu thông thường?
not2qubit

1
Tôi đã có /path/dir1/__init__.py và /path/dir1/mod.py. Đối với /path/some.py từ dir1.mod nhập func đã hoạt động. Khi trong /path/dir2/some.py nó chỉ hoạt động sau khi tôi sao chép và dán câu trả lời ở trên vào đầu tệp. Không muốn chỉnh sửa đường dẫn của tôi vì không phải mọi dự án python tôi có đều nằm trong / path /.
jwal

19

Xét applicationnhư thư mục gốc cho dự án python của bạn, tạo ra một sản phẩm nào __init__.pytập tin trong application, appfolderthư mục. Sau đó, bạn some_file.pythực hiện các thay đổi như sau để có được định nghĩa về func_name:

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()

nên là: sys.path.insert (0, r '/ from / root / thư mục')
Bolaka

18

Sử dụng sys.path.append với một đường dẫn tuyệt đối không lý tưởng khi di chuyển ứng dụng sang các môi trường khác. Sử dụng một đường dẫn tương đối sẽ không luôn hoạt động vì thư mục làm việc hiện tại phụ thuộc vào cách tập lệnh được gọi.

Vì cấu trúc thư mục ứng dụng đã được sửa, chúng tôi có thể sử dụng os.path để có được đường dẫn đầy đủ của mô-đun mà chúng tôi muốn nhập. Ví dụ: nếu đây là cấu trúc:

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

Và hãy nói rằng bạn muốn nhập mô-đun "xoài". Bạn có thể làm như sau trong vanilla.py:

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

Tất nhiên, bạn không cần biến mango_dir.

Để hiểu cách thức hoạt động này, hãy xem ví dụ về phiên tương tác này:

>>> 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'
>>> 

Và kiểm tra tài liệu os.path .


13

Điều này làm việc cho tôi trên windows

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

import some_file

10

Tôi khá đặc biệt: Tôi sử dụng Python với Windows!

Tôi chỉ điền thông tin: cho cả Windows và Linux, cả đường dẫn tương đối và tuyệt đối đều hoạt động sys.path(Tôi cần đường dẫn tương đối vì tôi sử dụng tập lệnh của mình trên một số PC và trong các thư mục chính khác nhau).

Và khi sử dụng cả Windows \/có thể được sử dụng làm dấu phân cách cho tên tệp và tất nhiên bạn phải nhân đôi \thành chuỗi Python,
một số ví dụ hợp lệ:

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

(lưu ý: Tôi nghĩ rằng /nó thuận tiện hơn \, sự kiện nếu nó ít 'Windows gốc' vì nó tương thích với Linux và đơn giản hơn để viết và sao chép vào Windows explorer)


4
os.path.join ('công cụ', 'mydir')
Corey Goldberg

7

Nếu mục đích tải mô-đun từ một đường dẫn cụ thể là để hỗ trợ bạn trong quá trình phát triển mô-đun tùy chỉnh, bạn có thể tạo một liên kết tượng trưng trong cùng thư mục của tập lệnh kiểm tra trỏ đến thư mục gốc của mô-đun tùy chỉnh. Tham chiếu mô-đun này sẽ được ưu tiên hơn bất kỳ mô-đun nào khác được cài đặt cùng tên cho bất kỳ tập lệnh nào chạy trong thư mục đó.

Tôi đã thử nghiệm điều này trên Linux nhưng nó sẽ hoạt động trong bất kỳ HĐH hiện đại nào hỗ trợ các liên kết tượng trưng.

Một lợi thế của phương pháp này là bạn có thể chỉ ra một mô-đun đang ngồi trong bản sao làm việc chi nhánh SVC cục bộ của bạn, điều này có thể đơn giản hóa rất nhiều thời gian chu kỳ phát triển và giảm các chế độ thất bại trong việc quản lý các phiên bản khác nhau của mô-đun.


7

Trong trường hợp của tôi, tôi đã có một lớp để nhập khẩu. Tập tin của tôi trông như thế này:

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

Trong tệp chính của tôi, tôi đã bao gồm mã thông qua:

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

1
@ not2qubit sys không được nhập trong câu trả lời.
Walter

7

Tôi đã va vào cùng một câu hỏi nhiều lần, vì vậy tôi muốn chia sẻ giải pháp của mình.

Phiên bản Python: 3.X

Giải pháp sau đây dành cho người phát triển ứng dụng của bạn trong Python phiên bản 3.X vì Python 2 không được hỗ trợ kể từ tháng 1/202020 .

Kết cấu dự án

Trong python 3, bạn không cần __init__.pytrong thư mục con dự án của mình do Gói không gian tên tiềm ẩn . Xem Is init .py không bắt buộc đối với các gói trong Python 3.3+

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

Báo cáo vấn đề

Trong file_b.py, tôi muốn nhập một lớp Atrong file_a.pythư mục a.

Các giải pháp

# 1 Một cách nhanh chóng nhưng bẩn thỉu

Không cài đặt gói như bạn hiện đang phát triển một dự án mới

Sử dụng try catchđể kiểm tra nếu các lỗi. Mã ví dụ:

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 Cài đặt gói của bạn

Khi bạn đã cài đặt ứng dụng của mình (trong bài đăng này, không bao gồm hướng dẫn cài đặt)

Bạn có thể đơn giản

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")

Chúc mừng mã hóa!


để biết thêm thông tin về nhập khẩu tuyệt đối
WY Hsu

3

Tôi đã làm việc với dự án amà tôi muốn người dùng cài đặt thông qua pip install adanh sách tệp sau:

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

thiết lập

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

Tôi đã cài đặt mô-đun bằng cách chạy các mục sau từ thư mục với MANIFEST.in:

python setup.py install

Sau đó, từ một vị trí hoàn toàn khác trên hệ thống tập tin của /moustache/armwrestletôi, tôi đã có thể chạy:

import a
dir(a)

Mà xác nhận rằng a.catsthực sự bằng 0 và a.b.dogsthực sự bằng 1, như dự định.


2

Thay vì chỉ làm một import ..., làm điều này:

from <MySubFolder> import <MyFile>

MyFile nằm trong MySubFolder.


1

Bạn có thể làm mới trình bao Python bằng cách nhấn f5 hoặc vào Run-> Run Module. Bằng cách này, bạn không phải thay đổi thư mục để đọc nội dung nào đó từ tệp. Python sẽ tự động thay đổi thư mục. Nhưng nếu bạn muốn làm việc với các tệp khác nhau từ thư mục khác nhau trong Python Shell, thì bạn có thể thay đổi thư mục trong sys, như Cameron đã nói trước đó.


1

Vì vậy, tôi đã nhấp chuột phải vào IDE của mình và thêm một cái mới foldervà tự hỏi tại sao tôi không thể nhập từ nó. Sau đó tôi nhận ra rằng tôi phải nhấp chuột phải và tạo Gói Python chứ không phải thư mục hệ thống tệp cổ điển. Hoặc một phương thức post-mortem đang thêm __init__.py(làm cho python coi thư mục hệ thống tệp như một gói) như được đề cập trong các câu trả lời khác. Thêm câu trả lời này ở đây chỉ trong trường hợp ai đó đã đi tuyến đường này.


1

Bạn có thể sử dụng importlib để nhập các mô-đun nơi bạn muốn nhập mô-đun từ một thư mục bằng cách sử dụng một chuỗi như sau:

import importlib

scriptName = 'Snake'

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

Ví dụ này có một main.txt là mã ở trên, sau đó là một thư mục có tên là ScScript và sau đó bạn có thể gọi bất cứ thứ gì bạn cần từ thư mục này bằng cách thay đổi scriptNamebiến. Sau đó bạn có thể sử dụng scriptđể tham chiếu đến mô-đun này. chẳng hạn như nếu tôi có một hàm được gọi Hello()trong mô-đun Snake, bạn có thể chạy hàm này bằng cách thực hiện:

script.Hello()

Tôi đã thử nghiệm điều này trong Python 3.6


0

Tôi đã có những vấn đề này một số lần. Tôi đã đến trang này rất nhiều. Trong vấn đề cuối cùng của tôi, tôi đã phải chạy servertừ một thư mục cố định, nhưng bất cứ khi nào gỡ lỗi tôi muốn chạy từ các thư mục con khác nhau.

import sys
sys.insert(1, /path) 

đã không làm việc cho tôi vì ở module khác nhau tôi đã phải đọc khác nhau * .csv file đó là tất cả trong cùng một thư mục.

Cuối cùng, những gì làm việc cho tôi không phải là pythonic, tôi đoán, nhưng:

Tôi đã sử dụng một phần if __main__ trên cùng của mô-đun tôi muốn gỡ lỗi , nó được chạy từ một đường dẫn khác với đường dẫn thông thường.

Vì thế:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.