Я пишу пакет Python со многими функциями, где я устанавливаю локальный импорт внутри них, чтобы избежать ошибки отсутствия пакета (чтобы другие мои функции могли работать, даже если у меня не установлен определенный пакет).
Тем не менее, я написал декоратор, чтобы обернуть некоторые из моих функций, но я не могу выполнить локальный импорт, если аргумент в декораторе требует определенного пакета. Аналогичная проблема с локальным импортом возникает, когда мне нужно импортировать определенный тип из другого пакета в аннотацию аргумента функции.
def do_fancy_stuff(arg):
def wrapped_func(func):
def inner_func():
print(f"Using {arg} on the target function!")
# Some other statements to define the inner function, for simplicity here I just run func()
func()
return inner_func
return wrapped_func
@do_fancy_stuff("A")
def some_func1():
import numpy
pass
@do_fancy_stuff("B")
def some_func2():
pass
# How can I avoid an import statement here and put it in the decorator?
# import numpy
@do_fancy_stuff(numpy.inf) # error here
def some_func3():
pass
# Similarly I need to import numpy outside the function if I want to do the annotation...
# import numpy
def other_func1(input_array : numpy.ndarray): # Error
pass
У меня есть идея использовать try-кроме всего импорта вне функции. Но это разрушит мою первоначальную идею о том, что все зависимости функции должны быть определены локально внутри этой функции.
Другая идея состоит в том, чтобы использовать exec
или написать небольшую функцию для его переноса, но, похоже, это делает код менее читаемым:
def _get_numpy_inf():
import numpy
return numpy.inf
Есть ли лучший способ сделать локальный импорт непосредственно в операторе декоратора и аннотации, например?
@do_fancy_stuff(import numpy; numpy.inf)
(import numpy; @do_fancy_stuff(numpy.inf) )
def other_func1(input_array : import numpy; numpy.ndarray):
# Just some imaginations, it doesn't work
Я попытаюсь дать ответ на ваш вопрос, но прежде чем я это сделаю, пожалуйста, выслушайте меня:
Если какой-либо пользователь вашего проекта устанавливает зависимости проекта с помощью файла требований, все зависимости должны быть доступны, что позволяет всем файлам и функциям работать должным образом.
Импорт модулей в начало файла — это хорошая практика программирования, которая упрощает и упрощает управление зависимостями файла, а также позволяет использовать эти модули вне функций.
Чтобы успешно управлять зависимостями проекта, обычно используют виртуальную среду и файл требований для любого проекта Python. Этот вид практики широко используется и в языках, отличных от Python.
Чтобы создать виртуальную среду в папке с именем venv
, используйте эту команду в своей оболочке: python -m venv venv
Чтобы активировать эту среду, используйте эту команду: source venv/bin/activate
Чтобы затем установить все зависимости вашего проекта, вы можете вызвать pip install -r requirements.txt
. Вы также можете выполнить этот шаг без использования виртуальной среды. Если вы решите сделать это таким образом, он установит зависимости в ваши глобальные модули Python. Это может быть нежелательно всегда, отсюда и существование виртуальных сред.
Файл требований очень прост. Он просто перечисляет ваши зависимости в отдельных строках. Это выглядит следующим образом:
numpy
matplotlib
another_dependency
И так далее...
Несмотря на то, что я действительно не рекомендую использовать структуру импорта, которая у вас есть в настоящее время, я предлагаю следующее решение:
Используйте другую функцию, чтобы создать область вокруг зависимого значения. Верните декорированную функцию из этой новой функции.
# How can I avoid an import statement here and put it in the decorator?
def some_func3():
import numpy
@do_fancy_stuff(numpy.inf) # error here
def f():
pass
return f
# Similarly I need to import numpy outside the function if I want to do the annotation...
def other_func1():
import numpy
def f(input_array : numpy.ndarray): # Error
pass
return f
Вызовите функции:
some_func3()()
other_func1()(numpy.ones(20))
Глядя на подобное решение, должно быть ясно, что синтаксис для использования этих функций станет неудобным и запутанным для чтения. Следуя приведенному выше руководству, ответ должен значительно облегчить вам жизнь в будущем :)
Я согласен, я попробую некоторые правки для более светлого тона :D
@YaakovBressler Я имею в виду, что этот ответ, кажется, из кожи вон лезет, чтобы не отталкивать. Больше, чем я считаю хорошим. Что бы вы предложили, в частности, для изменения тона?
Может быть, я так прочитал --> жирным шрифтом заголовки и тому подобное. @juanpa.arrivillaga --> при повторном прочтении я согласен, это уважительно.
Я считаю, что изменение предложений, чтобы предложить хорошие вещи, а не атаковать плохие вещи, делает это намного менее похожим на разговор вниз, поэтому я пошел с некоторыми из этих изменений.
Выглядит намного лучше. Отличное изменение с you should never...
на --> programmers should never
...
Спасибо за фантастический ответ! Мой пакет разработан так, чтобы принимать тип данных для построения графиков, но данные хранятся во многих различных форматах файлов. К сожалению, для каждого формата файла требуется определенный пакет для его чтения. Я стараюсь не заставлять пользователей устанавливать все пакеты (поскольку им может потребоваться отображать данные только в одном формате файла). Не лучше ли сделать весь импорт сверху и установить логическое значение, например HAS_FORMAT_A_SUPPORT, чтобы указать, может ли пользователь использовать определенную функцию?
Если количество требуемых модулей невелико (примерно 3 или 4), то установка всех модулей может быть не слишком большой проблемой, даже если им нужны только некоторые из них. Возможно, вы могли бы разделить разные типы форматов на разные файлы, чтобы они могли импортировать вверху, но фактически импортировать эти файлы только в том случае, если этот формат используется. Попробуйте такие идеи: stackoverflow.com/questions/6793748/… или другие, и используйте функции с одинаковыми именами в разных файлах. Может сделать то, что вам нужно :)
Полезно, но тон может быть неприятным, особенно для тех, кто плохо знаком с разработкой программного обеспечения. Могу я предложить сгладить более мягким тоном? (Ваш ответ очень полезен. Я хочу, чтобы он легко дошел до целевой аудитории.)