Есть ли способ сделать локальный импорт аргументов в декораторах Python и типов в аннотациях?

Я пишу пакет 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 есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я попытаюсь дать ответ на ваш вопрос, но прежде чем я это сделаю, пожалуйста, выслушайте меня:

Программисты никогда не должны писать код, предполагающий отсутствие зависимостей.

Если какой-либо пользователь вашего проекта устанавливает зависимости проекта с помощью файла требований, все зависимости должны быть доступны, что позволяет всем файлам и функциям работать должным образом.

Импорт модулей в начало файла — это хорошая практика программирования, которая упрощает и упрощает управление зависимостями файла, а также позволяет использовать эти модули вне функций.

Виртуальная среда

Чтобы успешно управлять зависимостями проекта, обычно используют виртуальную среду и файл требований для любого проекта 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))

Глядя на подобное решение, должно быть ясно, что синтаксис для использования этих функций станет неудобным и запутанным для чтения. Следуя приведенному выше руководству, ответ должен значительно облегчить вам жизнь в будущем :)

Полезно, но тон может быть неприятным, особенно для тех, кто плохо знаком с разработкой программного обеспечения. Могу я предложить сгладить более мягким тоном? (Ваш ответ очень полезен. Я хочу, чтобы он легко дошел до целевой аудитории.)

Yaakov Bressler 23.12.2020 00:32

Я согласен, я попробую некоторые правки для более светлого тона :D

Tyler 23.12.2020 00:39

@YaakovBressler Я имею в виду, что этот ответ, кажется, из кожи вон лезет, чтобы не отталкивать. Больше, чем я считаю хорошим. Что бы вы предложили, в частности, для изменения тона?

juanpa.arrivillaga 23.12.2020 00:39

Может быть, я так прочитал --> жирным шрифтом заголовки и тому подобное. @juanpa.arrivillaga --> при повторном прочтении я согласен, это уважительно.

Yaakov Bressler 23.12.2020 00:42

Я считаю, что изменение предложений, чтобы предложить хорошие вещи, а не атаковать плохие вещи, делает это намного менее похожим на разговор вниз, поэтому я пошел с некоторыми из этих изменений.

Tyler 23.12.2020 00:44

Выглядит намного лучше. Отличное изменение с you should never... на --> programmers should never...

Yaakov Bressler 23.12.2020 00:48

Спасибо за фантастический ответ! Мой пакет разработан так, чтобы принимать тип данных для построения графиков, но данные хранятся во многих различных форматах файлов. К сожалению, для каждого формата файла требуется определенный пакет для его чтения. Я стараюсь не заставлять пользователей устанавливать все пакеты (поскольку им может потребоваться отображать данные только в одном формате файла). Не лучше ли сделать весь импорт сверху и установить логическое значение, например HAS_FORMAT_A_SUPPORT, чтобы указать, может ли пользователь использовать определенную функцию?

Dau Zi 23.12.2020 00:51

Если количество требуемых модулей невелико (примерно 3 или 4), то установка всех модулей может быть не слишком большой проблемой, даже если им нужны только некоторые из них. Возможно, вы могли бы разделить разные типы форматов на разные файлы, чтобы они могли импортировать вверху, но фактически импортировать эти файлы только в том случае, если этот формат используется. Попробуйте такие идеи: stackoverflow.com/questions/6793748/… или другие, и используйте функции с одинаковыми именами в разных файлах. Может сделать то, что вам нужно :)

Tyler 23.12.2020 09:16

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