Как ссылаться на функцию более напрямую? - PullRequest
5 голосов
/ 27 января 2020

Список каталогов analyse структура, как показано ниже:

tree analyse
analyse
├── __init__.py
└── vix.py

__init__.py пусто, vix.py содержит функцию draw_vix.

import analyse
analyse.vix.draw_vix()

draw_vix ссылка на analyse.vix., я хочу сослаться на нее vix вместо analyse.vix, то есть добавить vix в пространство имен после import analyse.
Edit __init__.py:

import analyse.vix as vix
from analyse import vix

Тест:

import analyse
vix.draw_vix()

Сообщение об ошибке:

NameError: name 'vix' is not defined

@ hl037_, если я добавлю from .vix import vix в __init__.py,

import analyse
vix.draw_vix()

Это создает другую проблему: ImportError: невозможно импортировать имя 'vix'. Я хочу написать что-то в __init__.py или написать что-нибудь в vix.py, чтобы vix.draw_vix() вступил в силу.
Как это исправить?

Ответы [ 3 ]

5 голосов
/ 31 января 2020

добавить vix в пространство имен после анализа импорта

По умолчанию область верхнего уровня не отображается в модулях. Функция globals в модулях возвращает область действия уровня модуля, а не область импорта модуля (см. docs ). Поэтому некоторые трюки необходимы для помещения vix во внешнюю область.

Трюк один.

import Оператор в Python на самом деле является вызовом функции, а код из __init__.py выполняется в некотором стековом фрейме. Хитрость заключается в том, чтобы найти кадр стека, который соответствует импортирующему модулю, и загрязнить его область действия. Хотя это возможно, это ПЛОХАЯ, ПЛОХАЯ практика. Это противоречит парадигме Python, где простые вещи должны быть простыми. Если вам нужен vix в области верхнего уровня, используйте import analyse.vix as vix. Загрязнение области действия может привести к конфликту имен и очень тонким ошибкам.

Трюк второй.

Код из __init__.py выполняется только один раз, при первой загрузке пакета. Во всех других import analyse выражениях python интерпретатор использует объект модуля, кэшированный в sys.modules. Единственный найденный мной способ - перехватить вызовы встроенного __import__ и добавить ловушки, загрязняющие область.

Я настоятельно рекомендую избегать этого 'magi c' . Тем не менее, если вы хотите выстрелить в ногу, вот оно:

Поместите следующий код в analyse/__init__.py:

from . import vix # create module "vix" in the current scope

import inspect as _inspect

# Inject `vix` at the first import
_frame = _inspect.currentframe().f_back
while _frame is not None:
    # skip Pyhton module loader
    if "importlib._bootstrap" in _frame.f_code.co_filename:
        _frame = _frame.f_back
        continue
    #this frame corresponds to the scope, where `import analyse` is specified
    _frame.f_globals["vix"] = vix # bind the name 'vix' with the module 'vix' from the current scope
    print("DEBUG: injected vix to ", _frame)
    break
# remove _frame from the scope
del[_frame]

# overload __import__ function to inject `vix` on every succeeding import of `analyse`
import builtins as _builtins
# save builtin that `import` calls
_builtin_import = _builtins.__import__ 

def _never_overload_import(name, globals=None, locals=None, fromlist=(), level=0):
    "This function injects `vix` in every scope that imports `analyse` module"
    global _builtin_import
    global vix
    result = _builtin_import(name, globals, locals, fromlist, level)
    if name == "analyse" and globals is not None and not "vix" in globals:
        globals["vix"] = vix
        print("DEBUG: injected vix to ", _inspect.currentframe().f_back)
    return result

_builtins.__import__ = _never_overload_import

Вот как я тестировал:

  1. file secondary.py просто загружает целевой модуль.
import analyse
# demonstrate that first import created `vix` at this scope
vix.draw_vix("called from secondary as `vix.draw_vix`")
файл test.py загружает оба secondary.py и analyse
print("***Loading secondary***")
import secondary
print("***Secondary loaded***")

print("***Loading analyse***")
import analyse
print("***Analyse loaded***")

analyse.vix.draw_vix("called as `analyse.vix.draw_vix`")
vix.draw_vix("called as `vix.draw_vix`")

Выход:

***Loading secondary***
DEBUG: injected vix to  <frame at 0x00909AE8, file 'secondary.py', line 1, code <module>>
draw vix,  called from secondary as `vix.draw_vix`
***Secondary loaded***
***Loading analyse***
DEBUG: injected vix to  <frame at 0x00911C30, file 'test.py', line 6, code <module>>
***Analyse loaded***
draw vix,  called as `analyse.vix.draw_vix`
draw vix,  called as `vix.draw_vix`

CHANGELOG

Добавлена ​​перегрузка __import__ функция . Исходный ответ только что вставил объект vix в глобальные переменные импортируемой области, но этого недостаточно: код в __init__.py выполняется только один раз при первой загрузке пакета. Когда пакет импортирован в другой файл, он не увидит модуль vix, поскольку код внедрения не выполняется.

5 голосов
/ 27 января 2020

Вам просто не понравится это.

import analyse

сделает только 1004 * в вашем глобальном пространстве имен. В лучшем случае вы можете сделать:

from analyse import vix

, чтобы поместить vix в глобалы.

(или использовать звездное обозначение)

Бонус: если вы не хотите нужно проанализировать установленный (даже если с venv рекомендуется установить приложение / модуль внутри venv), чтобы импортировать vix внутри __init__.py, вы также можете сделать относительный импорт внутри:

from . import vix

https://docs.python.org/3/reference/import.html

[ПРАВИТЬ] Некоторые примеры:

Файловая система:

analyse
 ├─ __init__.py
 ├─ vix.py
 ├─ viz.py # another submodule to demonstrate some other points
 └─ vit.py # yet another submodule to demonstrate some other points

analyse/__init__.py содержит :

import .vit as vit
# from . import vit # works too
vix = 42
viy = 43

analyse/vix.py содержит:

def draw_vix():
  print('Hello from drax_vix')

analyse/viz.py содержит:

def draw_viz():
  print('Hello from drax_viz')

analyse/vit.py содержит:

def draw_vit():
  print('Hello from drax_vit')

Если ваш пакет установлен (например, вы создали файл setup.py и сделали pip install analyse) или дочерний элемент каталога, указанного в переменной env PYTHONPATH, в любом сценарии (или интерпретаторе python) снаружи пакет, эти вещи происходят:

1) полный импорт с псевдонимом:

import analyse.vix as vix
vix.draw_vix()
# works, and print print('Hello from drax_vix')
analyse
# Error : analyse not defined since you import only symbols from analyse.vix, not the modules themselves
analyse.vix
# Same as previous one

2) полный импорт

import analyse.vix
analyse.vix.draw_vix()
#Works because python guarantees analyse.vix to be a valid expression
analyse
# analyse module
vix.draw_vix()
# Error : vix undefined. import analyse.vix guarantee analyse.vix to be a valid module, but does not add submodule to global namespace
analyse.vix
# is a module, analyse.vix module has been imported too and set as an attribute of analyse.
analyse.viz
# Error : analyse has no attr viz since viz is a non-imported submodule.
analyse.vit.draw_vit
# Works because analyse is indirectly imported with import analyse.vix, and vit is imported as vit inside analyse/__init__.py and thus an attribute of the module.

3) импорт только анализ

import analyse
analyse.vix
# 42 because analyse.vix module is not imported, thus you saw the variable defined vix inside analyse
vix.draw_vix()
# Error : vix undefined, obviously, you never defined it nor imported it
vit.draw_vit()
# Works because vit is imported in __init__.py and set as an attribute of analyse.

4) Из анализа импо rt vix, viy, viz, vit

from analyse import vix, viz, vit
analyse.vix
# Error : analyse not define since you imported only symbols
vix
# 42 Because you didn't import analyse.vix and there is a variable vix defined in analyse/__init__.py
viy
# 43
viz
# module viz : viz is not declared as an attribute of analyse, but it falls back to the existing analyse.viz module
vit
# module vit : The one imported inside __init__.py (that is the same as analyse.vit)

5) из анализа импорта viy

from analyse import viy
# Error : analyse/viy.py does not exist
0 голосов
/ 05 февраля 2020

Попробуйте:

from analyse.vix import draw_vix()

Судя по другим ответам, это не то, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...