Як імпортувати клас Python, який знаходиться в каталозі вище?


Відповіді:


177

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 стосується абсолютного / відносного імпорту.


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

Pep 328 показує лише Python-версію: 2.4, 2,5, 2.6. Версія 3 залишається більш обізнаним душам.
gimel

22
Це викликає помилку ValueError: спроба відносного імпорту за межі пакету вищого рівня
Карло

4
Результати в ImportError: спроба відносного імпорту без відомого батьківського пакету
Ротків

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

1
це працює для мене. Додавши це, я можу безпосередньо імпортувати батьківські модулі, не потрібно використовувати "..".
Еван Ху

8
це працює лише, грубо кажучи, якщо значення PWD програми - його поточний directeroy, є дитиною батьків. Таким чином, навіть якщо це працює, багато видів системних змін можуть його зрушити.
Фліп

Це працювало для мене і для імпорту модулів вищого рівня. Я використовую це з os.chdir ("..") для завантаження інших файлів вищого рівня.
Surendra Shrestha

Це, здається, викликає ту саму помилку, що і вищевказана відповідь від gimel.
Карло

81

@ 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

2
це може додати каталог, який знаходиться всередині пакета Python, sys.pathтаким чином, щоб той самий модуль був доступний під різними іменами та всіма відповідними помилками. autopath.py в pypy або _preamble.py у крученому вирішіть це за допомогою критеріїв пошуку, що визначає пакет верхнього рівня, переходячи до каталогів вгору.
jfs

4
Ви можете зробити щось на зразок sys.path.remove(pathYouJustAdded)після потрібного імпорту, щоб не зберегти цей новий шлях.
Маттіас

35

Імпорт модуля з каталогу, який знаходиться на один рівень вище поточного каталогу:

from .. import module

38
Я отримав: спробу відносного імпорту за межі пакету вищого рівня :(
RicardoE

25
за допомогою from .. import module я отримав помилку ValueError: Спроба відносного імпорту в
непакеті,

4

Як завантажити модуль, який складається в каталог

передмова: Я істотно переписав попередню відповідь, сподіваючись допомогти людям ввійти в екосистему пітона, і сподіваюся, що всім найкраща зміна успіху в системі імпорту python.

Це стосуватиметься відносного імпорту в рамках пакету , що, на мою думку, є найбільш ймовірним випадком на питання ОП.

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 не пов'язаний з файловою системою

Ось чому ми можемо вбудовувати 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 умовно зберігає інформацію метаданих про вихідний код у межах, таких як:

  • які залежності потрібні для встановлення з назвою "install_requires"
  • яке ім'я слід використовувати для управління пакетом (встановити / видалити "ім'я"), я пропоную це відповідати вашому основного імені пакета python у нашому випадку foo , хоча заміна підкреслення для дефісів є популярною
  • інформація про ліцензування
  • теги зрілості (альфа / бета / тощо),
  • теги аудиторії (для розробників, для машинного навчання тощо),
  • вміст документації на одній сторінці (як README),
  • імена оболонки (імена, які ви вводите в оболонку користувача, як bash, або імена, які ви знайдете в графічній оболонці користувача, як меню запуску)
  • список модулів python, який цей пакет встановить (і видалить)
  • точка входу «запустити тести» за замовчуванням 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

-5

Python - це модульна система

Python не покладається на файлову систему

Щоб надійно завантажити код 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мій короткий приклад:

setup.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_модулі та пакети

foo_script.py

#!/usr/bin/env python3

import bar
import foo

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

2
Слід зазначити, що я завжди встановлюю свій модуль, коли працюю над ним pip install --edit foo, майже завжди всередині virtualenv. Я майже ніколи не пишу модуль, який не передбачається встановлювати. якщо я неправильно розумію щось, що хотів би знати.
ThorSummoner

Я також повинен зазначити, що використання тестового набору для автоматичного створення venv, подібного токсику, дуже корисно, оскільки editableпосилання на яйця та встановлені модулі пітона не є абсолютно однаковими у всіх напрямках; Наприклад, додавання нового простору імен до редагованого модуля буде знайдено у вашому пошуку шляху, але якщо це не експортується у вашому файлі setup.py, воно не буде упаковано / встановлено! Перевірте свій випадок використання :)
ThorSummoner
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.