Я хочу успадкувати від класу у файлі, який лежить у каталозі над поточним.
Чи можна відносно імпортувати цей файл?
Я хочу успадкувати від класу у файлі, який лежить у каталозі над поточним.
Чи можна відносно імпортувати цей файл?
Відповіді:
from ..subpkg2 import mod
За документами Python: Коли всередині ієрархії пакета використовуйте дві крапки, як говорить документ імпорту документа:
Вказуючи, який модуль імпортувати, не потрібно вказувати абсолютну назву модуля. Якщо модуль або пакунок містяться в іншому пакеті, можна здійснити відносний імпорт всередині того самого верхнього пакету, не згадуючи імені пакета. Використовуючи провідні точки у вказаному модулі чи пакеті,
from
ви зможете вказати, наскільки високою є обхід поточної ієрархії пакетів, не вказуючи точних назв. Одна провідна точка означає поточний пакет, де існує модуль, що здійснює імпорт. Дві крапки означає один рівень упаковки . Три точки - це два рівні тощо. Отже, якщо ви виконаєтеfrom . import mod
з модуля вpkg
пакеті, тоді ви закінчите імпортуватиpkg.mod
. Якщо ви виконуєте . Специфікація відносного імпорту міститься в межахfrom ..subpkg2 import mod
зсередини,pkg.subpkg1
ви імпортуєтеpkg.subpkg2.mod
PEP 328 .
PEP 328 стосується абсолютного / відносного імпорту.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
@ gimel відповідь правильна, якщо ви можете гарантувати ієрархію пакунків, яку він згадує. Якщо ви не можете - якщо ваша реальна потреба така, як ви її висловили, прив'язана виключно до каталогів і не має ніякого необхідного відношення до упаковки - тоді вам потрібно працювати над тим, __file__
щоб дізнатися батьківський каталог (пара os.path.dirname
дзвінків зробиться; -), а потім (якщо цей каталог не включене sys.path
) перед ім'ям тимчасової вставки сказала реж на самому початку sys.path
, __import__
, видалити знову сказав реж - брудну роботи на самому справі, але, «коли ви повинні, ви повинні» (і Pyhon прагне ніколи не перешкоджайте програмісту робити те, що потрібно зробити - як саме стандарт ISO C говорить у розділі "Дух С" у передмові! -).
Ось приклад, який може працювати для вас:
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
таким чином, щоб той самий модуль був доступний під різними іменами та всіма відповідними помилками. autopath.py в pypy або _preamble.py у крученому вирішіть це за допомогою критеріїв пошуку, що визначає пакет верхнього рівня, переходячи до каталогів вгору.
sys.path.remove(pathYouJustAdded)
після потрібного імпорту, щоб не зберегти цей новий шлях.
передмова: Я істотно переписав попередню відповідь, сподіваючись допомогти людям ввійти в екосистему пітона, і сподіваюся, що всім найкраща зміна успіху в системі імпорту python.
Це стосуватиметься відносного імпорту в рамках пакету , що, на мою думку, є найбільш ймовірним випадком на питання ОП.
Ось чому ми пишемо import foo
для завантаження модуля "foo" з кореневого простору імен, а не для написання:
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 у середовище, де немає файлової системи дефакто, не надаючи віртуальну, наприклад, Jython.
Розв’язаний з файлової системи дозволяє імпорту бути гнучким, цей дизайн дозволяє такі речі, як імпорт з архівів / zip-файлів, імпортувати одинакових клавіш, кешування байтових кодів, розширення cffi, навіть завантаження визначення коду.
Отже, якщо імпорт не пов'язаний з файловою системою, що означає "один каталог"? Нам потрібно вибрати деякі евристики, але ми можемо це зробити, наприклад, працюючи в рамках пакету , деякі евристики вже визначені, що робить відносний імпорт подібним .foo
і..foo
працює в межах одного пакету. Класно!
Якщо ви щиро хочете з'єднати шаблони завантаження вихідного коду з файловою системою, можете це зробити. Вам доведеться вибрати власну евристику та використовувати якусь імпортну техніку, рекомендую імпортну importlib
Приклад importlib Python виглядає приблизно так:
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
Офіційний проект-приклад доступний тут: https://github.com/pypa/sampleproject
Пакет python - це сукупність інформації про ваш вихідний код, яка може інформувати інші інструменти, як скопіювати свій вихідний код на інші комп’ютери та як інтегрувати свій вихідний код у шлях до системи, щоб він import foo
працював для інших комп'ютерів (незалежно від інтерпретатора, хост-операційна система тощо)
Дозволяє мати назву пакета foo
в деякому каталозі (бажано, порожній каталог).
some_directory/
foo.py # `if __name__ == "__main__":` lives here
Я вважаю за краще створити setup.py
як рідний брат foo.py
, оскільки це спрощує написання файла setup.py простішим, проте ви можете написати конфігурацію, щоб змінити / перенаправити все, що налаштування робить за замовчуванням; наприклад, розміщення foo.py
під каталогом "src /" є дещо популярним, тут не висвітлено.
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/
"редаговане" ака -e
ще знову перенаправить імпортуючу машину для завантаження вихідних файлів у цей каталог, замість цього скопіюючи поточні точні файли в бібліотеку середовища інсталяції. Це також може спричинити відмінності в поведінці на машині розробника, не забудьте протестувати свій код! Є інші інструменти, крім pip, проте я рекомендую, щоб pip був вступним :)
Мені також подобається зробити foo
"пакунок" (каталог, що містить __init__.py
) замість модуля (єдиний файл ".py"), і "пакети", і "модулі" можна завантажувати в кореневу область імен, модулі дозволяють вкладені простори імен, що корисно, якщо ми хочемо імпортувати "відносний один каталог".
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
Мені також подобається зробити це foo/__main__.py
, це дозволяє python виконувати пакет як модуль, наприклад python3 -m foo
, виконуватиме foo/__main__.py
як __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',
]
},
)
Дозволяє обгрунтувати це з допомогою декількох модулів: В основному, ви можете мати структуру каталогів так:
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
умовно зберігає інформацію метаданих про вихідний код у межах, таких як:
foo
, хоча заміна підкреслення для дефісів є популярноюpython ./setup.py test
Це дуже експансивно, він може навіть збирати розширення c на льоту, якщо на розроблювальній машині встановлюється вихідний модуль. Для прикладу щодня я рекомендую setup.py репозиторію зразків PYPA
Якщо ви випускаєте артефакт збірки, наприклад копію коду, який призначений для роботи майже однакових комп'ютерів, файл вимоги.txt - це популярний спосіб знімати точну інформацію про залежність, де "install_requires" - хороший спосіб зафіксувати мінімум та максимально сумісні версії. Однак, враховуючи, що цільові машини в будь-якому випадку майже однакові, я настійно рекомендую створити тарбол цілого префікса python. Це може бути складним, надто детальним, щоб потрапити сюди. Перевірте pip install
«S--target
варіант, або virtualenv ака venv для потенційних клієнтів.
повернутися до прикладу
З foo / spam / egg.py, якщо нам потрібен код з foo / baz, ми могли б попросити його за його абсолютним простором імен:
import foo.baz
Якщо ми хотіли б зарезервувати можливість переміщення egg.py в якийсь інший каталог в майбутньому з іншою відносною baz
реалізацією, ми могли б використовувати відносний імпорт, наприклад:
import ..baz
Щоб надійно завантажити код python, встановіть цей код у модулі та цей модуль, встановлений у бібліотеці python.
Встановлені модулі завжди можна завантажувати з простору імен верхнього рівня import <name>
Офіційно представлений чудовий зразок проекту: https://github.com/pypa/sampleproject
В основному, ви можете мати структуру каталогів так:
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.
.
Обов'язково оголосити setuptools.setup()
по прибуттю setup.py
,
офіційний приклад: https://github.com/pypa/sampleproject/blob/master/setup.py
У нашому випадку ми, мабуть, хочемо експортувати, bar.py
і foo/__init__.py
мій короткий приклад:
#!/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
)
.
Тепер ми можемо встановити наш модуль у бібліотеку python; за допомогою pip ви можете встановити the_foo_project
у свою бібліотеку python в режимі редагування, щоб ми могли над цим працювати в режимі реального часу
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
.
Тепер з будь-якого контексту python ми можемо завантажити наші спільні py_модулі та пакети
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
, майже завжди всередині virtualenv. Я майже ніколи не пишу модуль, який не передбачається встановлювати. якщо я неправильно розумію щось, що хотів би знати.
editable
посилання на яйця та встановлені модулі пітона не є абсолютно однаковими у всіх напрямках; Наприклад, додавання нового простору імен до редагованого модуля буде знайдено у вашому пошуку шляху, але якщо це не експортується у вашому файлі setup.py, воно не буде упаковано / встановлено! Перевірте свій випадок використання :)