Nhập hàm cục bộ từ một mô-đun được đặt trong một thư mục khác với các lần nhập tương đối trong Máy tính xách tay Jupyter bằng Python 3


126

Tôi có cấu trúc thư mục tương tự như sau

meta_project
    project1
        __init__.py
        lib
            module.py
            __init__.py
    notebook_folder
        notebook.jpynb

Khi làm việc trong notebook.jpynbnếu tôi cố gắng sử dụng nhập tương đối để truy cập một hàm function()trong module.py:

from ..project1.lib.module import function

Tôi nhận được lỗi sau đây:

SystemError                               Traceback (most recent call last)
<ipython-input-7-6393744d93ab> in <module>()
----> 1 from ..project1.lib.module import function

SystemError: Parent module '' not loaded, cannot perform relative import

Có cách nào để điều này hoạt động bằng cách sử dụng nhập khẩu tương đối không?

Lưu ý, máy chủ sổ ghi chép được khởi tạo ở cấp độ meta_projectthư mục, vì vậy nó phải có quyền truy cập vào thông tin trong các tệp đó.

Ngoài ra, lưu ý rằng ít nhất như dự định ban đầu project1không được coi là một mô-đun và do đó không có __init__.pytệp, nó chỉ có nghĩa là một thư mục hệ thống tệp. Nếu giải pháp cho vấn đề yêu cầu coi nó như một mô-đun và bao gồm một __init__.pytệp (thậm chí một tệp trống) thì cũng được, nhưng làm như vậy là không đủ để giải quyết vấn đề.

Tôi chia sẻ thư mục này giữa các máy và quá trình nhập tương đối cho phép tôi sử dụng cùng một mã ở mọi nơi và tôi thường sử dụng sổ ghi chép để tạo mẫu nhanh, vì vậy các đề xuất liên quan đến việc hack cùng nhau các đường dẫn tuyệt đối không có khả năng hữu ích.


Chỉnh sửa: Điều này không giống như Nhập tương đối trong Python 3 , nói về nhập tương đối trong Python 3 nói chung và - đặc biệt - chạy một tập lệnh từ bên trong một thư mục gói. Điều này liên quan đến việc làm việc trong sổ ghi chép jupyter cố gắng gọi một hàm trong mô-đun cục bộ trong một thư mục khác có cả khía cạnh chung và riêng khác nhau.


1
__init__tệp nào trong thư mục gói của bạn không?
Iron Fist

Có, trong libthư mục.
mpacer

Xin vui lòng, đề cập đến nó trong cấu trúc thư mục của bạn trong câu hỏi của bạn
Iron Fist

Chỉ cần thực hiện chỉnh sửa đó ngay khi tôi nhìn thấy bình luận đầu tiên của bạn :). Cảm ơn bạn đã nắm bắt được điều đó.
mpacer

Có thể có bản sao của Nhập tương đối trong Python 3
Baldr

Câu trả lời:


173

Tôi đã có ví dụ gần giống như bạn trong sổ tay này , nơi tôi muốn minh họa việc sử dụng chức năng của mô-đun liền kề theo cách KHÔ.

Giải pháp của tôi là cho Python biết đường dẫn nhập mô-đun bổ sung đó bằng cách thêm một đoạn mã như sau vào sổ ghi chép:

import os
import sys
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)

Điều này cho phép bạn nhập chức năng mong muốn từ hệ thống phân cấp mô-đun:

from project1.lib.module import function
# use the function normally
function(...)

Lưu ý rằng cần thêm __init__.pycác tệp trống vào thư mục project1 /lib / nếu bạn chưa có chúng.


6
Điều này giải quyết vấn đề có thể nhập một gói bằng cách sử dụng ít nhiều một vị trí tương đối, nhưng chỉ gián tiếp. Tôi tình cờ biết Matthias Bussonier (@matt trên SE) và Yuvi Panda (@yuvi trên SE) đang phát triển github.com/ipython/ipynb sẽ giải quyết vấn đề này trực tiếp hơn (ví dụ: bằng cách cho phép nhập tương đối bằng cú pháp chuẩn sau khi gói của họ được nhập khẩu). Tôi sẽ chấp nhận câu trả lời của bạn ngay bây giờ và khi giải pháp của họ hoàn toàn sẵn sàng cho những người khác sử dụng, tôi có thể sẽ viết câu trả lời về cách sử dụng nó hoặc yêu cầu một trong số họ làm như vậy.
mpacer

cảm ơn vì đã chỉ ra init .py trống. Tôi là người mới bắt đầu python và gặp sự cố khi nhập các lớp của mình. Tôi đã nhận được ghi chú mô-đun tìm thấy lỗi, thêm init .py trống đã khắc phục sự cố!
Pat Grady

5
Trống init tập tin py không còn cần thiết bằng Python 3.
CathyQian


25

Đến đây để tìm kiếm các phương pháp hay nhất trong việc trừu tượng hóa mã thành các mô-đun con khi làm việc trong Notebooks. Tôi không chắc rằng có một phương pháp hay nhất. Tôi đã được đề xuất điều này.

Một hệ thống phân cấp dự án như vậy:

├── ipynb
   ├── 20170609-Examine_Database_Requirements.ipynb
   └── 20170609-Initial_Database_Connection.ipynb
└── lib
    ├── __init__.py
    └── postgres.py

Và từ 20170609-Initial_Database_Connection.ipynb:

    In [1]: cd ..

    In [2]: from lib.postgres import database_connection

Điều này hoạt động vì theo mặc định, Máy tính xách tay Jupyter có thể phân tích cú pháp cdlệnh. Lưu ý rằng điều này không sử dụng phép thuật Python Notebook. Nó chỉ đơn giản là hoạt động mà không cần chi tiêu trước %bash.

Xét rằng 99 lần trong số 100 Tôi đang làm việc trong Docker bằng một trong những hình ảnh dự án Jupyter Docker , việc sửa đổi sau đây idempotent

    In [1]: cd /home/jovyan

    In [2]: from lib.postgres import database_connection

Cảm ơn. Thực sự kinh khủng những hạn chế của nhập khẩu tương đối này.
Michael

Tôi cũng sử dụng chdirthay vì thêm vào đường dẫn, vì tôi vừa quan tâm đến việc nhập từ repo chính cũng như giao tiếp với một số tệp ở đó.
TheGrimmScientist

Đáng buồn thay, điều bị hack nhiều nhất mà tôi làm trong python. Tuy nhiên, tôi không thể tìm thấy một giải pháp tốt hơn.
TheGrimmScientist

cho tính năng đơn giản (cho phép cùng một ô chạy nhiều lần & nhận được cùng một kết quả) if os.path.isdir('../lib/'): os.chdir('../lib'):; hoặc, tốt hơn, sử dụng ../lib/db/với của bạn postgres.pyđể không vô tình chdir đến một thư mục cao hơn cũng chứa một thư mục khác lib.
michael

1
Tôi thích giải pháp này cho đến khi tôi vô tình thực hiện cd ..hai lần.
minhle_r7 23/03/18

15

Cho đến nay, câu trả lời được chấp nhận đã phù hợp nhất với tôi. Tuy nhiên, mối quan tâm của tôi luôn là có khả năng xảy ra trường hợp mà tôi có thể cấu trúc lại notebooksthư mục thành các thư mục con, yêu cầu thay đổi module_pathtrong mỗi sổ ghi chép. Tôi quyết định thêm một tệp python trong mỗi thư mục sổ ghi chép để nhập các mô-đun cần thiết.

Do đó, có cấu trúc dự án như sau:

project
|__notebooks
   |__explore
      |__ notebook1.ipynb
      |__ notebook2.ipynb
      |__ project_path.py
   |__ explain
       |__notebook1.ipynb
       |__project_path.py
|__lib
   |__ __init__.py
   |__ module.py

Tôi đã thêm tệp project_path.pyvào từng thư mục con sổ tay ( notebooks/explorenotebooks/explain). Tệp này chứa mã để nhập tương đối (từ @metakermit):

import sys
import os

module_path = os.path.abspath(os.path.join(os.pardir, os.pardir))
if module_path not in sys.path:
    sys.path.append(module_path)

Bằng cách này, tôi chỉ cần nhập tương đối trong project_path.pytệp chứ không phải trong sổ ghi chép. Các tệp sổ tay sau đó sẽ chỉ cần nhập project_pathtrước khi nhập lib. Ví dụ trong 0.0-notebook.ipynb:

import project_path
import lib

Lưu ý ở đây là việc đảo ngược nhập khẩu sẽ không hiệu quả. ĐIỀU NÀY KHÔNG HOẠT ĐỘNG:

import lib
import project_path

Vì vậy cần phải cẩn thận trong quá trình nhập khẩu.


3

Tôi vừa tìm thấy giải pháp hay này:

import sys; sys.path.insert(0, '..') # add parent folder path where lib folder is
import lib.store_load # store_load is a file on my library folder

Bạn chỉ muốn một số chức năng của tệp đó

from lib.store_load import your_function_name

Nếu phiên bản python> = 3.3, bạn không cần tệp init.py trong thư mục


3
Tôi thấy điều này rất hữu ích. Tôi sẽ nói thêm rằng sửa đổi sau sẽ được thêm vào ->if ".." not in sys.path: ... sys.path.insert(0,"..")
Yaakov Bressler

2

Tự mình nghiên cứu chủ đề này và đọc các câu trả lời, tôi khuyên bạn nên sử dụng thư viện path.py vì nó cung cấp trình quản lý ngữ cảnh để thay đổi thư mục làm việc hiện tại.

Sau đó bạn có một cái gì đó giống như

import path
if path.Path('../lib').isdir():
    with path.Path('..'):
        import lib

Mặc dù, bạn có thể bỏ qua isdircâu lệnh.

Ở đây tôi sẽ thêm các câu lệnh in để dễ dàng theo dõi những gì đang xảy ra

import path
import pandas

print(path.Path.getcwd())
print(path.Path('../lib').isdir())
if path.Path('../lib').isdir():
    with path.Path('..'):
        print(path.Path.getcwd())
        import lib
        print('Success!')
print(path.Path.getcwd())

kết quả đầu ra trong ví dụ này (trong đó lib ở /home/jovyan/shared/notebooks/by-team/data-vis/demos/lib):

/home/jovyan/shared/notebooks/by-team/data-vis/demos/custom-chart
/home/jovyan/shared/notebooks/by-team/data-vis/demos
/home/jovyan/shared/notebooks/by-team/data-vis/demos/custom-chart

Vì giải pháp sử dụng trình quản lý ngữ cảnh, bạn được đảm bảo quay lại thư mục làm việc trước đó của mình, bất kể hạt nhân của bạn ở trạng thái nào trước ô và bất kể trường hợp ngoại lệ nào được đưa ra khi nhập mã thư viện của bạn.


Điều này sẽ không hoạt động kết hợp với% tự động tải lại, vì đường dẫn mô-đun sẽ không được tìm thấy tại thời điểm tải lại
Johannes

1

Đây là 2 xu của tôi:

nhập hệ thống

ánh xạ đường dẫn nơi chứa tệp mô-đun. Trong trường hợp của tôi, đó là máy tính để bàn

sys.path.append ('/ Users / John / Desktop')

Nhập toàn bộ mô-đun ánh xạ NHƯNG sau đó bạn phải sử dụng .notation để ánh xạ các lớp như ánh xạ.Shipping ()

import mapping # mapping.py là tên tệp mô-đun của tôi

shipit = mapping.Shipment () #Shipment là tên của lớp tôi cần sử dụng trong mô-đun ánh xạ

Hoặc nhập lớp cụ thể từ mô-đun ánh xạ

từ ánh xạ nhập khẩu Ánh xạ

shipit = Lô hàng () #Bây giờ bạn không cần phải sử dụng .notation


0

Tôi nhận thấy rằng python-dotenv giúp giải quyết vấn đề này khá hiệu quả. Cấu trúc dự án của bạn sẽ thay đổi một chút, nhưng mã trong sổ ghi chép của bạn đơn giản hơn một chút và nhất quán trên các sổ ghi chép.

Đối với dự án của bạn, hãy cài đặt một chút.

pipenv install python-dotenv

Sau đó, dự án thay đổi thành:

├── .env (this can be empty)
├── ipynb
   ├── 20170609-Examine_Database_Requirements.ipynb
   └── 20170609-Initial_Database_Connection.ipynb
└── lib
    ├── __init__.py
    └── postgres.py

Và cuối cùng, quá trình nhập của bạn sẽ thay đổi thành:

import os
import sys

from dotenv import find_dotenv


sys.path.append(os.path.dirname(find_dotenv()))

Điểm +1 cho gói này là sổ ghi chép của bạn có thể chứa nhiều thư mục. python-dotenv sẽ tìm cái gần nhất trong thư mục mẹ và sử dụng nó. Điểm +2 cho cách tiếp cận này là jupyter sẽ tải các biến môi trường từ tệp .env khi khởi động. Gấp đôi.

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.