Làm cách nào để nhập một lớp Python có trong một thư mục ở trên?


Câu trả lời:


175

from ..subpkg2 import mod

Theo các tài liệu Python: Khi bên trong một hệ thống phân cấp gói, hãy sử dụng hai dấu chấm, như tài liệu câu lệnh nhập nói:

Khi chỉ định mô-đun cần nhập, bạn không phải chỉ định tên tuyệt đối của mô-đun. Khi một mô-đun hoặc gói được chứa trong một gói khác, có thể thực hiện nhập tương đối trong cùng một gói hàng đầu mà không phải đề cập đến tên gói. Bằng cách sử dụng các dấu chấm hàng đầu trong mô-đun hoặc gói được chỉ định sau khi frombạn có thể chỉ định mức độ cao để vượt qua phân cấp gói hiện tại mà không chỉ định tên chính xác. Một dấu chấm hàng đầu có nghĩa là gói hiện tại nơi mô-đun thực hiện nhập. Hai dấu chấm có nghĩa là lên một cấp gói . Ba dấu chấm lên hai cấp, v.v ... Vì vậy, nếu bạn thực thi from . import modtừ một mô-đun trong pkggói thì cuối cùng bạn sẽ nhập pkg.mod. Nếu bạn thực hiện from ..subpkg2 import modtừ bên trong, pkg.subpkg1bạn sẽ nhậppkg.subpkg2.mod. Thông số kỹ thuật cho nhập khẩu tương đối được bao gồm trong PEP 328 .

PEP 328 thỏa thuận với hàng nhập khẩu tuyệt đối / tương đối.


4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.

Pep 328 chỉ hiển thị Python-Phiên bản: 2.4, 2.5, 2.6. Phiên bản 3 được để lại cho những linh hồn hiểu biết hơn.
gimel

22
Điều này gây ra lỗi ValueError: đã cố nhập tương đối ngoài gói cấp cao nhất
Carlo

4
Kết quả trong ImportError: đã thử nhập tương đối mà không có gói cha đã biết
Rotkiv

115
import sys
sys.path.append("..") # Adds higher directory to python modules path.

1
cái này hiệu quả với tôi Sau khi thêm phần này, tôi có thể nhập trực tiếp các mô đun mẹ, không cần sử dụng "..".
Evan Hu

8
điều đó chỉ hoạt động, đại khái, nếu giá trị PWD của ứng dụng - chỉ thị hiện tại của nó, là con của cha mẹ. Vì vậy, ngay cả khi nó hoạt động, nhiều loại thay đổi hệ thống có thể đánh bật nó.
Phlip

Điều này làm việc cho tôi cũng như để nhập các mô-đun cấp cao hơn. Tôi sử dụng điều này với os.chdir ("..") để tải các tệp khác ở cấp độ cao hơn.
Surendra Shrestha

Điều này dường như gây ra lỗi tương tự như câu trả lời ở trên từ gimel.
Carlo

81

Câu trả lời của @ gimel là chính xác nếu bạn có thể đảm bảo hệ thống phân cấp gói mà anh ấy đề cập. Nếu bạn không thể - nếu nhu cầu thực sự của bạn là như bạn đã thể hiện, chỉ gắn liền với các thư mục và không có bất kỳ mối quan hệ cần thiết nào với việc đóng gói - thì bạn cần phải __file__tìm ra thư mục mẹ (một vài os.path.dirnamecuộc gọi sẽ thực hiện; -), sau đó (nếu thư mục đó chưa được bật sys.path) hãy tạm thời chèn thư mục đã nói vào lúc bắt đầu sys.path, __import__hãy xóa thư mục đã nói lại - thực sự công việc lộn xộn, nhưng, "khi bạn phải, bạn phải" (và Pyhon cố gắng không bao giờ ngăn lập trình viên làm những việc phải làm - giống như tiêu chuẩn ISO C đã nói trong phần "Spirit of C" trong lời nói đầu của nó! -).

Đây là một ví dụ có thể phù hợp với bạn:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir

2
điều này có thể thêm một thư mục nằm trong gói Python để sys.pathcung cấp cùng một mô-đun dưới các tên khác nhau và tất cả các lỗi tương ứng. autopath.py trong pypy hoặc _preamble.py trong xoắn giải quyết nó bằng cách sử dụng tiêu chí tìm kiếm xác định gói cấp cao nhất trong khi di chuyển các thư mục lên trên.
JFS

4
Bạn có thể muốn làm một cái gì đó như sys.path.remove(pathYouJustAdded)sau khi nhập mà bạn cần để không giữ lại đường dẫn mới này.
Matthias

35

Nhập mô-đun từ một thư mục chính xác một cấp trên thư mục hiện tại:

from .. import module

38
Tôi đã nhận: đã cố gắng nhập tương đối ngoài gói cấp cao nhất :(
RicardoE

25
bằng cách sử dụng from .. import module Tôi gặp lỗi ValueError: Đã cố nhập tương đối trong gói không theo gói theo khuyến nghị
user3428154 14/11/18

3

Làm thế nào để tải một mô-đun là một thư mục lên

lời nói đầu: Tôi đã viết lại đáng kể câu trả lời trước đó với hy vọng giúp mọi người dễ dàng vào hệ sinh thái của python và hy vọng mang lại cho mọi người sự thay đổi thành công tốt nhất với hệ thống nhập khẩu của python.

Điều này sẽ bao gồm nhập khẩu tương đối trong một gói , mà tôi nghĩ là trường hợp có thể xảy ra nhất đối với câu hỏi của OP.

Python là một hệ thống mô-đun

Đây là lý do tại sao chúng ta viết import foođể tải một mô-đun "foo" từ không gian tên gốc, thay vì viết:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python không được ghép nối với một hệ thống tệp

Đây là lý do tại sao chúng ta có thể nhúng python vào môi trường không có hệ thống tập tin defacto mà không cung cấp một ảo, chẳng hạn như Jython.

Được tách rời khỏi hệ thống tệp cho phép nhập linh hoạt, thiết kế này cho phép những thứ như nhập từ tệp lưu trữ / tệp zip, nhập singletons, bộ đệm ẩn mã byte, tiện ích mở rộng cffi, thậm chí tải định nghĩa mã từ xa.

Vì vậy, nếu nhập không được kết hợp với một hệ thống tập tin, "một thư mục lên" có nghĩa là gì? Chúng tôi phải chọn ra một số heuristic nhưng chúng tôi có thể làm điều đó, ví dụ như khi làm việc trong một gói , một số đã được xác định làm cho nhập khẩu tương đối giống .foo..foo hoạt động trong cùng một gói. Mát mẻ!

Nếu bạn thực sự muốn ghép các mẫu tải mã nguồn của mình vào một hệ thống tệp, bạn có thể làm điều đó. Bạn sẽ phải chọn heuristic của riêng bạn, và sử dụng một số loại máy móc nhập khẩu, tôi khuyên bạn nên phỏng

Ví dụ importlib của Python trông giống như vậy:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

Bao bì

Có một dự án ví dụ tuyệt vời có sẵn chính thức tại đây: https://github.com/pypa/sampleproject

Gói python là tập hợp thông tin về mã nguồn của bạn, có thể thông báo cho các công cụ khác cách sao chép mã nguồn của bạn sang các máy tính khác và cách tích hợp mã nguồn của bạn vào đường dẫn của hệ thống đó để import foohoạt động cho các máy tính khác (bất kể trình thông dịch, hệ điều hành máy chủ, v.v.)

Cấu trúc thư mục

Cho phép có một tên gói foo, trong một số thư mục (tốt nhất là một thư mục trống).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Sở thích của tôi là tạo setup.pynhư anh chị em của mình foo.py, vì nó làm cho việc viết tệp setup.py đơn giản hơn, tuy nhiên bạn có thể viết cấu hình để thay đổi / chuyển hướng mọi thứ setuptools làm theo mặc định nếu bạn muốn; ví dụ: đặt foo.pytrong thư mục "src /" là hơi phổ biến, không được đề cập ở đây.

some_directory/
    foo.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

"có thể chỉnh sửa" aka -esẽ một lần nữa chuyển hướng máy móc nhập để tải các tệp nguồn trong thư mục này, thay vào đó sao chép các tệp chính xác hiện tại vào thư viện của môi trường cài đặt. Điều này cũng có thể gây ra sự khác biệt về hành vi trên máy của nhà phát triển, hãy chắc chắn kiểm tra mã của bạn! Có những công cụ khác ngoài pip, tuy nhiên tôi muốn giới thiệu pip là công cụ giới thiệu :)

Tôi cũng muốn tạo foomột "gói" (một thư mục chứa __init__.py) thay vì một mô-đun (một tệp ".py"), cả "gói" và "mô-đun" có thể được tải vào không gian tên gốc, các mô-đun cho phép các không gian tên lồng nhau, sẽ hữu ích nếu chúng ta muốn nhập "thư mục tương đối lên".

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

Tôi cũng muốn tạo một foo/__main__.py, điều này cho phép python thực thi gói dưới dạng một mô-đun, ví dụ python3 -m foosẽ thực thi foo/__main__.pynhư __main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

Cho phép bổ sung thêm một số mô-đun: Về cơ bản, bạn có thể có cấu trúc thư mục như vậy:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py thông thường giữ thông tin siêu dữ liệu về mã nguồn bên trong, chẳng hạn như:

  • những phụ thuộc nào là cần thiết để cài đặt có tên là "install_Vquires"
  • Tên nào sẽ được sử dụng để quản lý gói (cài đặt / gỡ cài đặt "tên"), tôi đề nghị tên này khớp với tên gói python chính của bạn trong trường hợp của chúng tôi foo , mặc dù việc thay thế dấu gạch dưới cho dấu gạch nối là phổ biến
  • thông tin cấp phép
  • thẻ đáo hạn (alpha / beta / etc),
  • thẻ đối tượng (cho nhà phát triển, cho máy học, v.v.),
  • nội dung tài liệu một trang (như README),
  • tên shell (tên bạn nhập tại shell người dùng như bash hoặc tên bạn tìm thấy trong shell người dùng đồ họa như menu bắt đầu),
  • một danh sách các mô-đun python gói này sẽ cài đặt (và gỡ cài đặt)
  • một điểm nhập cảnh "chạy thử" python ./setup.py test

Nó rất mở rộng, nó thậm chí có thể biên dịch các phần mở rộng c một cách nhanh chóng nếu một mô-đun nguồn đang được cài đặt trên một máy phát triển. Đối với một ví dụ hàng ngày, tôi khuyên bạn nên thiết lập Trình lưu trữ mẫu của PYPA

Nếu bạn đang phát hành một tạo phẩm xây dựng, ví dụ: một bản sao của mã có nghĩa là để chạy các máy tính gần như giống hệt nhau, thì tệp tests.txt là một cách phổ biến để chụp nhanh thông tin phụ thuộc chính xác, trong đó "install_Vquires" là một cách tốt để nắm bắt tối thiểu và phiên bản tương thích tối đa. Tuy nhiên, do các máy mục tiêu gần như giống hệt nhau, tôi khuyên bạn nên tạo một tarball của toàn bộ tiền tố python. Điều này có thể là khó khăn, quá chi tiết để có được vào đây. Kiểm tra pip install's--target lựa chọn, hoặc virtualenv aka venv tức.

trở lại ví dụ

Làm thế nào để nhập một tập tin một thư mục lên:

Từ foo / spam / egg.py, nếu chúng tôi muốn mã từ foo / baz, chúng tôi có thể yêu cầu nó bằng không gian tên tuyệt đối của nó:

import foo.baz

Nếu chúng tôi muốn dự trữ khả năng để di chuyển egg.py vào một số thư mục khác trong tương lai với một số baztriển khai tương đối khác , chúng tôi có thể sử dụng nhập tương đối như:

import ..baz

-5

Python là một hệ thống mô-đun

Python không dựa vào hệ thống tệp

Để tải mã python một cách đáng tin cậy, hãy đặt mã đó trong một mô-đun và mô-đun đó được cài đặt trong thư viện của python.

Các mô-đun đã cài đặt luôn có thể được tải từ không gian tên cấp cao nhất với import <name>


Có một dự án mẫu tuyệt vời có sẵn chính thức tại đây: https://github.com/pypa/sampleproject

Về cơ bản, bạn có thể có một cấu trúc thư mục như vậy:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

Hãy chắc chắn để khai báo của bạn setuptools.setup()trong setup.py,

ví dụ chính thức: https://github.com/pypa/sampleproject/blob/master/setup.py

Trong trường hợp của chúng tôi, chúng tôi có thể muốn xuất khẩu bar.pyfoo/__init__.py, ví dụ ngắn gọn của tôi:

thiết lập

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

Bây giờ chúng ta có thể cài đặt mô-đun của mình vào thư viện python; Với pip, bạn có thể cài đặt the_foo_projectvào thư viện python của mình ở chế độ chỉnh sửa, vì vậy chúng tôi có thể làm việc với nó trong thời gian thực

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

Bây giờ từ bất kỳ bối cảnh python nào, chúng ta có thể tải các gói và gói py_modules được chia sẻ của mình

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))

2
nên đề cập đến việc tôi luôn cài đặt mô-đun của mình trong khi tôi làm việc với nó pip install --edit foo, hầu như luôn luôn bên trong một virtualenv. Tôi gần như không bao giờ viết một mô-đun không có ý định cài đặt. nếu tôi hiểu nhầm điều gì đó tôi muốn biết.
ThorSummoner

Tôi cũng nên đề cập rằng việc sử dụng bộ kiểm tra tạo tự động venv, như tox rất hữu ích vì editablecác liên kết trứng và các mô-đun python được cài đặt không hoàn toàn giống nhau về mọi mặt; ví dụ: thêm một không gian tên mới vào mô-đun có thể chỉnh sửa sẽ được tìm thấy trong tra cứu đường dẫn của bạn, nhưng nếu điều đó không được xuất trong tệp setup.py của bạn thì nó sẽ không được đóng gói / cài đặt! Kiểm tra trường hợp sử dụng của bạn :)
ThorSummoner
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.