Могу ли я получить имя файла, который импортировал текущий файл, и извлечь его классы? [dубликат]

Предположим, у меня есть два модуля:

first.package.module

# Assign module to a variable
which_module = ???

print("I originally live in {}".format(__name__))   # Prints first.package.module
print("I was run from {}".format(which_module))

first.package.module

from first.package.module import *

third.package.module

from first.package.module import *

Как я могу получить вторую строку, «я был запущен», чтобы напечатать second.package.module после импорта в файле second.package.module? Или third.package.module, когда он запускается оттуда.

Причина, по которой я хочу это поведение, связана с созданием приложения в Django, которое используется несколько раз в одном проекте, т. Е. Есть несколько примеров то же приложение. Каждый из этих экземпляров имеет свои собственные модели, которые наследуются от абстрактной модели. Чтобы создавать многократно используемые виды и URL-адреса, я хочу динамически загружать модели, и для этого мне нужно знать, какой модуль импортировал приложение и запускал его.

1
задан 3 August 2015 в 22:22

1 ответ

Вы можете напечатать имя модуля, запускающего загрузку модуля, с помощью:

import sys

print sys._getframe(1).f_globals['__name__']

. В Python 3 с новым стеком importlib вам нужно увеличить кадрэкран до 6 (по крайней мере для 3.5, 3.6 и 3.7):

print(sys._getframe(6).f_globals['__name__'])

Решение кросс-CPython должно отфильтровывать любые importlib._bootstrap* элементы в стеке:

import sys

def imported_from(depth=0):
    # skip the frames of this function, and the caller
    f = sys._getframe(2 + depth)
    while f and f.f_code.co_filename.startswith("<frozen importlib._bootstrap"):
        f = f.f_back
    return f and f.f_globals['__name__']

print(imported_from())

Выше imported_from() может быть определена в модуле утилиты и импортирована; вызов sys._getframe(2) гарантирует, что исходный контекст является тем, что вызвало вызов imported_from(). Укажите положительное целое число в качестве аргумента depth, чтобы увеличить количество пропущенных кадров. Функция работает на любой версии Python, которая предоставляет функцию sys._getframe(), независимо от того, использует ли она стек importlib.

Обратите внимание, что это:

Опирается на деталь реализации CPython (публикация стека кадров недоступна для других реализаций Python). См. Документацию функции sys._getframe(): деталь реализации CPython: эта функция должна использоваться только для внутренних и специализированных целей. Он не гарантированно существует во всех реализациях Python. В Python 3 это зависит от конкретных деталей реализации стека importlib; текущая разработка может добавлять или удалять вызовы в этом стеке. Например, в Python 3.3, который впервые ввел importlib, правильное количество стека для пропуска равно 9, в 3.4 оно уменьшилось до 7, а 3.5 упало до 6 (число, которое с тех пор стабильно). imported_from() работает вокруг этого, но вводит новое зависящее от реализации предположение: что стековые фреймы, связанные с импортом, можно обнаружить, ища префикс <frozen importlib._bootstrap в имени файла. Теоретически можно скомпилировать CPython с importlib._bootstrap, оставшимся незамерзающим (не включенным в двоичный файл интерпретатора как массив данных marshal). Работает только тогда, когда модуль импортируется в первый раз, после чего Python выполняет код модуля для создания объекта sys.modules.

Я не могу подчеркнуть последнюю точку. Python только когда-либо загружает модуль один раз. Импорт - это двухэтапный процесс: загрузка и привязка. Шаг загрузки выполняется только в том случае, если объект модуля еще не существует, все остальные импорты для модуля связывают только имена.

Названия привязок - это не то, что вы можете практически подключить; import modulename - это не более чем назначение modulename = sys.modules['modulename'] после того, как модуль уже находится в памяти.

3
ответ дан 15 August 2018 в 16:02
  • 1
    Спасибо за ответ. Это, однако, дает мне importlib._bootstrap . – Christoffer Karlsson 3 August 2015 в 22:11
  • 2
    @ChristofferKarlsson: в Python 3 вам придется увеличить количество кадров. – Martijn Pieters♦ 3 August 2015 в 22:14
  • 3
    @ChristofferKarlsson: как я уже сказал: вы не можете . Это не то, как работает импорт Python. Python загружает код только один раз, а затем связывает имена с загруженным объектом. Он не будет повторно выполнять ваш код для каждого оператора импорта. – Martijn Pieters♦ 3 August 2015 в 22:24
  • 4
    @ M.I.Wright: он полностью зависит от деталей реализации текущей версии, поэтому нет. Но это правильный номер для Python 3.5, 3.6 и 3.7. – Martijn Pieters♦ 3 August 2018 в 16:13
  • 5
    @ M.I.Wright: Я обновил ответ, чтобы подчеркнуть это. – Martijn Pieters♦ 3 August 2018 в 16:18

Другие вопросы по тегам:

Похожие вопросы: