Nhập python từ thư mục anh chị em

Đặt đoạn mã này ở đầu bất kỳ tệp Python nào không phải là cấp cao nhất trong dự án của bạn để nhập để gọi mã từ các thư mục khác

Đoạn trích

if __name__ == '__main__':
    import os
    import sys
    sys.path.append(os.getcwd())

Mục đích

Dự án này nhằm mục đích làm ví dụ về cách nhập các gói anh chị em khi phát triển bằng Python. Nó dựa trên 3. 6. 8, tôi tin rằng nó sẽ hoạt động trên mọi thứ trên Python 3. 3 dựa trên Ghi chú của Nick Coghlan

Tôi không có cách nào khẳng định đây là "Pythonic" hay đúng cách để làm cho nó hoạt động, nhưng tôi đã có thể làm cho nó hoạt động ổn định cho các nỗ lực phát triển khác của mình và nghĩ rằng những người khác có thể thấy ví dụ này hữu ích

Python có tài liệu về cách xử lý quá trình nhập, nhưng tôi thấy các ví dụ này không hữu ích trong quá trình phát triển dự án. Các ví dụ sử dụng đường dẫn tuyệt đối dường như cho rằng bạn đang thử nghiệm từ cấp cao nhất của dự án và không chạy các mô-đun riêng lẻ gọi một mô-đun được tìm thấy trong thư mục khác. Ngoài ra còn có các ví dụ về Nhập khẩu tương đối, tuy nhiên, đây dường như chỉ là một cách để tránh đánh vần đường dẫn đầy đủ của các mô-đun của bạn và không phải là thứ bạn có thể tận dụng trong quá trình phát triển

Dự án mẫu

Bao gồm bên dưới là bố cục dự án và các ví dụ về nơi bạn sẽ chèn đoạn mã từ phía trên. Dự án này cũng chứa các tệp để bạn có thể tự kiểm tra kết quả của việc này

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}

from sub1.helper import helper
from sub2.utils import some_func
3 có thể gọi xuống các gói thấp hơn mà không gặp sự cố. Tuy nhiên, nếu bạn muốn chạy
from sub1.helper import helper
from sub2.utils import some_func
4 với lệnh gọi tới
from sub1.helper import helper
from sub2.utils import some_func
5, điều này yêu cầu thực hiện thêm công việc để thực hiện chức năng. Bạn có thể sử dụng Đoạn trích từ phía trên ở đầu mã cấp thấp hơn của mình để chạy riêng lẻ và vẫn tận dụng được nội dung được viết trong thư mục khác

Làm thế nào nó hoạt động

Điều này tận dụng chức năng HĐH Python để lấy Thư mục làm việc hiện tại. Khi nó có thư mục làm việc hiện tại của bạn, nó sẽ thêm nó vào Đường dẫn hệ thống, đây là một biến chứa tất cả các vị trí tìm kiếm cho các gói/mô-đun sẽ được tải. Khi tải các gói khác trong dự án của bạn, nó sẽ xử lý chúng như thể bạn đang làm việc từ thư mục cấp cao nhất

Đoạn trích giả định rằng bạn đang mở dự án này trong môi trường phát triển của mình từ cấp cao nhất hoặc gốc của dự án và có thể không hoạt động theo cách khác. Để sửa đổi mã được mở từ cấp thấp hơn của gói, chẳng hạn như trong

from sub1.helper import helper
from sub2.utils import some_func
6 trong Âm thanh thay vì PYTHON-SIBLING-IMPORT, cần phải sửa đổi các câu lệnh nhập để loại bỏ việc bao gồm
from sub1.helper import helper
from sub2.utils import some_func
7

Sở thích của tôi là sử dụng bố cục dự án sau đây, trong đó

from sub1.helper import helper
from sub2.utils import some_func
3 và
from sub1.helper import helper
from sub2.utils import some_func
4 là các gói con độc lập

project/
    main.py
    sub1/
        __init__.py
        helper.py
    sub2/
        __init__.py
        utils.py
    tests/
        __init__.py
        test_sub1.py
        test_sub2.py

Các tệp bên dưới

from sub1.helper import helper
from sub2.utils import some_func
5 được sử dụng để kiểm tra từng gói/mô-đun. Chúng tôi có thể nhập từ mỗi gói như dưới đây

from sub1.helper import helper
from sub2.utils import some_func

Chạy một mô-đun thử nghiệm duy nhất

Để chạy một mô-đun thử nghiệm, trong trường hợp này là

from sub1.helper import helper
from sub2.utils import some_func
6

________số 8

Lưu ý rằng lệnh này đang chạy từ thư mục

from sub1.helper import helper
from sub2.utils import some_func
7, không phải bên trong thư mục
from sub1.helper import helper
from sub2.utils import some_func
8. Ngoài ra, hãy cẩn thận rằng đó là
from sub1.helper import helper
from sub2.utils import some_func
9, không phải
$ python -m tests.test_sub1
0. Điều này là để tham chiếu một mô-đun thử nghiệm giống như cách bạn nhập nó

Chạy một trường hợp thử nghiệm hoặc phương pháp thử nghiệm

Ngoài ra, bạn có thể chạy một TestCase hoặc một phương pháp thử nghiệm duy nhất

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
3

Chạy tất cả các bài kiểm tra

Một cách là sử dụng

$ python -m tests.test_sub1
1 được đề cập trong Cấu trúc thư mục điển hình để chạy thử nghiệm bằng cách sử dụng unittest, như được sao chép bên dưới

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
5

Điều này sẽ chạy tất cả các bài kiểm tra *. mô-đun py bên trong gói

from sub1.helper import helper
from sub2.utils import some_func
8

Ngoài ra, chúng ta có thể cài đặt pytest. Sau đó để chạy tất cả các bài kiểm tra

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
7

Tùy chọn thứ hai ngắn gọn hơn tùy chọn thứ nhất

Chạy một mô-đun trong gói con

Để chạy một mô-đun bên trong gói con, trong trường hợp này là

$ python -m tests.test_sub1
3. chúng tôi không thể sử dụng

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
9

Thay vào đó, chúng ta nên sử dụng kỹ thuật tương tự như chạy mô-đun thử nghiệm

from sub1.helper import helper
from sub2.utils import some_func
0

Nhập từ một thư mục anh chị em

Đôi khi, chúng tôi có thể muốn nhập một mô-đun/phương thức từ thư mục anh chị em. Ví dụ:

$ python -m tests.test_sub1
4 muốn nhập
$ python -m tests.test_sub1
5. Sử dụng

from sub1.helper import helper
from sub2.utils import some_func
3

Sau đó, hãy nhớ chạy

$ python -m tests.test_sub1
4 dưới dạng mô-đun, như đã đề cập ở trên

Nhập từ thư mục mẹ

Xem xét cách bố trí sau,

$ python -m tests.test_sub1
7 được sử dụng bởi hầu hết các tệp nguồn. Ví dụ:
$ python -m tests.test_sub1
4 muốn nhập
$ python -m tests.test_sub1
7 từ thư mục mẹ

from sub1.helper import helper
from sub2.utils import some_func
8

Trong

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
30, chúng tôi không thể trực tiếp sử dụng

from sub1.helper import helper
from sub2.utils import some_func
0

Tùy chọn một là thêm một

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
31 bên dưới
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
32 để chuyển đổi dự án gốc thành một gói và chạy
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
33 từ bên ngoài thư mục
from sub1.helper import helper
from sub2.utils import some_func
7. Tuy nhiên, có một số nhược điểm. Đầu tiên, thật bất tiện khi gọi từ bên ngoài với một lệnh dài như vậy. Thứ hai, điều này giả định rằng dự án gốc là một gói, điều này không phải lúc nào cũng đúng

Tùy chọn hai là tạo một virtualenv và sử dụng

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
35 với một
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
36 thích hợp. Trong trường hợp này,
from sub1.helper import helper
from sub2.utils import some_func
7 có thể được cài đặt cục bộ dưới dạng gói. Vì vậy, nó không được áp dụng khi dự án không phải là một gói

Một lựa chọn tốt hơn là di chuyển

$ python -m tests.test_sub1
7 vào một gói con mới, chẳng hạn như
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
39, bao gồm
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
31 và
$ python -m tests.test_sub1
7. Sau đó, chúng tôi chuyển vấn đề này thành trường hợp nhập từ gói anh chị em, có giải pháp ở trên. Quan trọng hơn, tùy chọn này cũng hoạt động khi dự án gốc không phải là một gói

Tùy chọn cuối cùng là hack

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
52 nếu chúng tôi không muốn chuyển
$ python -m tests.test_sub1
7 sang gói con cũng như biến dự án thành một gói. Dựa trên bài đăng trên blog này và câu hỏi stackoverflow của Remi, chúng tôi có thể đặt mã bên dưới vào một tệp có tên là
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
54

from sub1.helper import helper
from sub2.utils import some_func
1

Sau đó, trong

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
30, trước tiên chúng tôi nhập nó sau đó nhập các mô-đun khác

from sub1.helper import helper
from sub2.utils import some_func
2

Ghi chú. thuộc tính

python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
56 không phải lúc nào cũng được cung cấp. Thay vì sử dụng
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
57, Remi đề xuất sử dụng mô-đun kiểm tra để truy xuất tên tệp (và đường dẫn) của tệp hiện tại. Sau khi hack
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
52, cả
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
59 và
python-sibling-import/
    top_level.py
    sound/
        second_level.py
        effects/
            echo.py
        formats/
            wav_write.py
                {snippet}
                {your code}
70 đều hợp lệ