Python - импорт в if

Я написал небольшую обертку для urllib (python3). Является правильным и безопасным для импорта модуля, если?

if self.response_encoding == 'gzip':
    import gzip

Я не нашел PEP об этом коде. Однако это беспокоит меня.

Ответ 1

Стандартная библиотека Python использует его, поэтому он определенно правилен и безопасен. Смотрите отличный источник os:

if 'posix' in _names:
    name = 'posix'
    linesep = '\n'
    from posix import *
    try:
        from posix import _exit
    except ImportError:
      pass
    import posixpath as path
    import posix
    __all__.extend(_get_exports_list(posix))
    del posix

Это довольно распространено для условного импорта модулей в python. Вместо if вы также увидите комбинацию try:/except ImportError::

try:
    from subprocess import check_output
except ImportError:
    # Python 2.6 and before
    def check_output(*popenargs, **kwargs):
        from subprocess import Popen
        if 'stdout' in kwargs:
            raise ValueError('stdout argument not allowed, it will be '
                             'overridden.')
        process = Popen(stdout=PIPE, *popenargs, **kwargs)
        output, unused_err = process.communicate()
        retcode = process.poll()
        if retcode:
            cmd = kwargs.get("args")
            if cmd is None:
                cmd = popenargs[0]
            raise CalledProcessError(retcode, cmd)
        return output

Здесь мы в основном используем моральный эквивалент теста if: если вы можете импортировать check_output, сделайте это, иначе определите полную функцию здесь.

Оператор import - это просто перевязка внешнего фрагмента кода с локальным именем. Использование потока управления if для управления импортом ничем не отличается от назначения переменной в инструкции if в этом отношении. Вам нужно убедиться, что вы не используете имя без его определения в любом случае.

Ответ 2

На самом деле это довольно распространенная идиома. Иногда вы увидите, что он выбирает между различными модулями:

if system == 'linux':
   import linuxdeps as deps
elif system == 'win32':
   import win32deps as deps

Тогда, если оба linuxdeps и win32deps имеют одинаковые функции, вы можете просто использовать его:

deps.func()

Это даже используется для получения os.path в стандартной библиотеке (следующий исходный код для os):

if 'posix' in _names:
    name = 'posix'
    linesep = '\n'
    from posix import *
    try:
        from posix import _exit
    except ImportError:
        pass
    import posixpath as path

    import posix
    __all__.extend(_get_exports_list(posix))
    del posix

elif 'nt' in _names:
    name = 'nt'
    linesep = '\r\n'
    from nt import *
    try:
        from nt import _exit
    except ImportError:
        pass
    import ntpath as path

    import nt
    __all__.extend(_get_exports_list(nt))
    del nt

Ответ 3

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

Ответ 4

Это безопасно? Да. Как ответ Martijin указал, что официальный Python использует это.

Это правильно? Зависит. Python docs указывает, что даже несмотря на то, что python может избежать импорта одного и того же модуля, все еще накладные расходы.

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