Tôi muốn kế thừa từ một lớp trong một tệp nằm trong một thư mục phía trên tệp hiện tại.
Có thể nhập tương đối tập tin đó?
Tôi muốn kế thừa từ một lớp trong một tệp nằm trong một thư mục phía trên tệp hiện tại.
Có thể nhập tương đối tập tin đó?
Câu trả lời:
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
from
bạ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 thifrom . import mod
từ một mô-đun trongpkg
gói thì cuối cùng bạn sẽ nhậppkg.mod
. Nếu bạn thực hiệnfrom ..subpkg2 import mod
từ bên trong,pkg.subpkg1
bạ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.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
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.dirname
cuộ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
sys.path
cung 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.
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.
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
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ị
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.
Đâ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
Đâ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
và..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
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 foo
hoạ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.)
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.py
như 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.py
trong 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 -e
sẽ 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 foo
mộ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 foo
sẽ thực thi foo/__main__.py
như __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ư:
foo
, mặc dù việc thay thế dấu gạch dưới cho dấu gạch nối là phổ biếnpython ./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ụ
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ố baz
triển khai tương đối khác , chúng tôi có thể sử dụng nhập tương đối như:
import ..baz
Để 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.py
và foo/__init__.py
, ví dụ ngắn gọn của tôi:
#!/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_project
và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
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
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.
editable
cá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 :)