Nhập tệp từ thư mục con?


456

Tôi có một tập tin được gọi tester.py, nằm trên /project.

/projectcó một thư mục con được gọi lib, với một tệp được gọi là BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Tôi muốn nhập BoxTimetừ tester. Tôi đã thử điều này:

import lib.BoxTime

Kết quả nào:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

Bất kỳ ý tưởng làm thế nào để nhập BoxTimetừ thư mục con?

BIÊN TẬP

Các __init__.pylà vấn đề, nhưng đừng quên đề cập đến BoxTimenhư lib.BoxTime, hoặc sử dụng:

import lib.BoxTime as BT
...
BT.bt_function()

Câu trả lời:


536

Hãy xem tài liệu Gói (Phần 6.4) tại đây: http://docs.python.org/tutorial/modules.html

Nói tóm lại, bạn cần đặt một tập tin trống có tên

__init__.py

trong thư mục "lib".


59
Tại sao nó cảm thấy hack ? Đó là cách python đánh dấu các thư mục nhập khẩu an toàn / có sẵn.
Tôi chấp nhận

7
Nó không chỉ đánh dấu các thư mục nhập an toàn / có sẵn, mà còn cung cấp một cách để chạy một số mã khởi tạo khi nhập tên thư mục.
Sadjad

32
Vâng, đây là hacky và thậm chí bẩn, và theo tôi, ngôn ngữ không nên áp đặt cách tải tập tin của nó trên hệ thống tập tin. Trong PHP, chúng tôi đã giải quyết vấn đề bằng cách cho phép mã vùng người dùng đăng ký nhiều hàm tự động tải được gọi khi thiếu không gian tên / lớp. Sau đó, cộng đồng đã sản xuất tiêu chuẩn PSR-4 và Nhà soạn nhạc thực hiện nó, và ngày nay không ai phải lo lắng về điều đó. Và không có __init__tập tin mã hóa ngu ngốc nào (nhưng nếu bạn muốn, chỉ cần đăng ký một móc tự động tải! Đây là sự khác biệt giữa hackyhackable ).
Morgan Todarey Quilling

4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman

4
python là một mớ hỗn độn :)
Jimmy Pettersson

174
  • Tạo một thư mục con có tên lib.
  • Tạo một tập tin trống có tên lib\__init__.py.
  • Trong lib\BoxTime.py, viết một hàm foo()như thế này:

    def foo():
        print "foo!"
    
  • Trong mã máy khách của bạn trong thư mục trên lib, hãy viết:

    from lib import BoxTime
    BoxTime.foo()
    
  • Chạy mã khách hàng của bạn. Bạn sẽ nhận được:

    foo!

Rất lâu sau - trong linux, nó sẽ trông như thế này:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!

2
Bạn có thể cung cấp một liên kết đến tài liệu Python nơi giải thích điều này không? Cảm ơn!
Zenon

5
Hãy tạo liên kết đó có thể nhấp được: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples

Hướng dẫn tuyệt vời để triển khai góilib
MasterControlProgram

xin lưu ý: các thư mục con sẽ không chứa dấu gạch ngang hoặc dấu chấm, nhưng dấu gạch dưới là hợp lệ. đối với tôi có vẻ giống như các hạn chế như đối với các tên biểu tượng khác, nhưng tôi chưa đào sâu nó xuống cấp độ tài liệu.
Alexander Stohr

gạch dưới => python3 (quá muộn để chỉnh sửa nhận xét)
Alexander Stohr

68

Bạn có thể thử chèn nó vào sys.path:

sys.path.insert(0, './lib')
import BoxTime

11
Điều này thật tuyệt nếu bạn vì một số lý do không thể hoặc sẽ không tạo tệp init .py.
jpihl

1
Nó hoạt động nếu bạn chạy python từ thư mục "dự án". Các "." được diễn giải liên quan đến thư mục làm việc hiện tại của bạn, không liên quan đến thư mục nơi tệp bạn đang thực thi. Giả sử bạn cd /data, python ../project/tester.py. Sau đó, nó sẽ không hoạt động.
sáng

2
Điều này làm việc cho tôi. Tôi thích điều này hơn một tập tin init .py, nó làm cho các câu lệnh nhập sạch hơn.
Taylor Evanson

5
Điều này hoạt động tốt hơn và là giải pháp "chính xác". init .py làm rối các gói như boto có các thư mục con riêng với các mô-đun.
Dave Dopson

1
@jpihl Bạn phải tạo (ít nhất) một tệp empy có tên __init__.py để cho phép các mô-đun nhập python từ thư mục đó. Tôi đã thử giải pháp đó và hoạt động hoàn hảo (v2.7.6).
m3nda

31

Tôi viết thư này vì mọi người dường như đề nghị bạn phải tạo một libthư mục.

Bạn không cần đặt tên thư mục con của bạn lib. Bạn có thể đặt tên cho nó anythingvới điều kiện bạn đặt một cái __init__.pyvào đó.

Bạn có thể làm điều đó bằng cách nhập lệnh sau trong shell linux:

$ touch anything/__init__.py 

Vì vậy, bây giờ bạn có cấu trúc này:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Sau đó, bạn có thể nhập mylibvào main.pynhư thế này:

from anything import mylib 

mylib.myfun()

Bạn cũng có thể nhập các hàm và các lớp như thế này:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

Bất kỳ hàm hoặc lớp biến nào bạn đặt bên trong __init__.pycũng có thể được truy cập:

import anything

print(anything.myvar)

Hoặc như thế này:

from anything import myvar

print(myvar)

Cấu trúc thư mục của tôi là utils\__init__.pyutils\myfile.py. (Utils chứa cả hai tệp) Đây là cách tôi đang cố gắng nhập from utils.myfile import myMethod. Nhưng tôi nhận được ModuleNotFoundError: No module named 'utils'. điều gì sai? Tái bút: Tôi đang sử dụng Djangovà cố gắng nhập trong views.pyđó ở cùng cấp độ với utilsthư mục
Jagruti 18/03/19

Có thể sử dụng các đường dẫn tuyệt đối khi nhập mô-đun và chạy chương trình của bạn vớiPYTHONPATH=. python path/to/program.py
Nurettin

21

Thư mục lib của bạn có chứa một __init__.pytập tin không?

Python sử dụng __init__.pyđể xác định nếu một thư mục là một mô-đun.


16

Hãy thử import .lib.BoxTime. Để biết thêm thông tin đọc về nhập khẩu tương đối trong PEP 328 .


2
Tôi không nghĩ rằng tôi đã từng thấy cú pháp được sử dụng trước đây. Có lý do mạnh mẽ (không) để sử dụng phương pháp này?
tgray

2
Tại sao đây không phải là câu trả lời. Chắc chắn, nếu bạn muốn làm toàn bộ gói, bạn nên làm điều đó. Nhưng đó không phải là câu hỏi ban đầu.
Travis Griggs

Điều này mang lại cho tôi: ValueError: Đã cố nhập tương đối trong gói không
Alex

5
Điều này chỉ hoạt động nếu tệp bạn nhập từ chính nó là một phần của gói. Nếu không, bạn sẽ nhận được lỗi mà @Alex đã chỉ ra.
Jonathon Reinhart

8

Tôi làm điều này về cơ bản bao gồm tất cả các trường hợp (đảm bảo bạn có __init__.pythư mục tương đối / đường dẫn / đến / your / lib /):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Ví dụ:
Bạn có trong thư mục dự án của bạn:

/root/myproject/app.py

Bạn có trong một thư mục dự án khác:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Bạn muốn sử dụng /root/anotherproject/utils.pyvà gọi hàm foo có trong đó.

Vì vậy, bạn viết trong app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()

2
nếu bạn đang sử dụng, os.pathcó lẽ bạn muốn sử dụng os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')thay vì mã hóa '/' trong nối đường dẫn của bạn.
cowbert

Tại sao bạn không thể làm "../anotherproject"mà không có os.path.dirname()?
Moshe Rabaev

@MosheRabaev - Nên sử dụng các chức năng os.path. Trong trường hợp viết "../anotherproject" và chuyển mã sang HĐH Windows, mã sẽ bị hỏng! os.path utils biết cách trả về đường dẫn chính xác khi xem xét hệ điều hành mà mã đang chạy. để biết thêm thông tin docs.python.org/2/l Library / os.path.html
Sao Thủy

@MosheRabaev và nếu bạn sử dụng ".." mà không có dirname(realpath(__file__)), thì nó sẽ tính toán đường dẫn liên quan đến thư mục làm việc hiện tại của bạn khi bạn chạy tập lệnh, không liên quan đến nơi tập lệnh sống.
TJ Ellis

5

Tạo một tệp trống __init__.pytrong thư mục con / lib. Và thêm vào đầu mã chính

from __future__ import absolute_import 

sau đó

import lib.BoxTime as BT
...
BT.bt_function()

hoặc tốt hơn

from lib.BoxTime import bt_function
...
bt_function()

0

Chỉ là một bổ sung cho những câu trả lời.

Nếu bạn muốn nhập tất cả các tệp từ tất cả các thư mục con , bạn có thể thêm tệp này vào thư mục gốc của tệp.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

Và sau đó bạn có thể chỉ cần nhập tệp từ thư mục con giống như những tệp này nằm trong thư mục hiện tại.

Ví dụ làm việc

Nếu tôi có thư mục sau với các thư mục con trong dự án của mình ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Tôi có thể đặt đoạn mã sau trong a.pytập tin của mình

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

Nói cách khác, mã này sẽ trừu tượng từ thư mục mà tệp đến từ đâu.


-1

/project/tester.py

/project/lib/BoxTime.py

tạo tập tin trống __init__.pyxuống dòng cho đến khi bạn đạt được tập tin

/project/lib/somefolder/BoxTime.py

#lib- nhu cầu có hai mục một __init__.pyvà thư mục có tên somefolder #somefoldercó hai mục boxtime.py__init__.py


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.