Как набрать подсказку с необязательным импортом? - PullRequest
1 голос
/ 23 апреля 2020

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

Чтобы привести простой пример с pandas в качестве необязательной зависимости:

def my_func() -> pd.DataFrame:                                                  
    import pandas as pd                                                         
    return pd.DataFrame()                                                       

df = my_func()

В этом случае, поскольку оператор import находится в пределах my_func, этот код, что неудивительно, вызовет:

NameError: имя 'pd' не определено

Если использовалась подсказка строкового литерала типа вместо этого т.е. :

def my_func() -> 'pd.DataFrame':                                                
    import pandas as pd                                                         
    return pd.DataFrame()                                                       

df = my_func()

модуль теперь может выполняться без проблем, но mypy будет выдавать сообщение:

ошибка: имя 'pd' не определено

Как сделать так, чтобы модуль успешно выполнялся, и сохранить возможность проверки типа stati c, но при этом этот импорт также может быть необязательным?

1 Ответ

1 голос
/ 23 апреля 2020

Попробуйте вставить свой импорт в оператор if typing.TYPE_CHECKING вверху файла. Эта переменная всегда ложна во время выполнения, но всегда обрабатывается как подсказка типа.

Например:

# Lets us avoid needing to use forward references everywhere
# for Python 3.7+
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import pandas as pd

def my_func() -> pd.DataFrame:  
    import pandas as pd                                                 
    return pd.DataFrame()

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

One Предостережение заключается в том, что, хотя pandas будет необязательной зависимостью во время выполнения, она все равно будет обязательной для проверки типов.

Другой вариант, который вы можете использовать, это флаги mypy --always-true и --always-false. Это дало бы вам более точный контроль над тем, какие части вашего кода проверяются по типу. Например, вы могли бы сделать что-то вроде этого:

try:
    import pandas as pd
    PANDAS_EXISTS = True
except ImportError:
    PANDAS_EXISTS = False

if PANDAS_EXISTS:
    def my_func() -> pd.DataFrame:                                                   
        return pd.DataFrame()

... затем выполните mypy --always-true=PANDAS_EXISTS your_code.py, чтобы напечатать проверку, предполагая, что pandas импортировано, и mypy --always-false=PANDAS_EXISTS your_code.py, чтобы проверить тип, предполагая, что она пропущена.

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

...