Как mypy использует typing.TYPE_CHECKING для решения проблемы циклического импорта аннотаций? - PullRequest
2 голосов
/ 01 мая 2020

У меня есть следующая структура пакета:

/prog
-- /ui
---- /menus
------ __init__.py
------ main_menu.py
------ file_menu.py
-- __init__.py
__init__.py
prog.py

Это мои операторы импорта / классов:

prog.py:

from prog.ui.menus import MainMenu

/ prog / ui / menus / __ init__.py:

from prog.ui.menus.file_menu import FileMenu
from prog.ui.menus.main_menu import MainMenu

main_menu.py:

import tkinter as tk
from prog.ui.menus import FileMenu

class MainMenu(tk.Menu):

    def __init__(self, master: tk.Tk, **kwargs):
        super().__init__(master, **kwargs)
        self.add_cascade(label='File', menu=FileMenu(self, tearoff=False))

    [...]

file_menu.py:

import tkinter as tk
from prog.ui.menus import MainMenu

class FileMenu(tk.Menu):

    def __init__(self, master: MainMenu, **kwargs):
        super().__init__(master, **kwargs)
        self.add_command(label='Settings')

    [...]

Это приведет к циклической проблеме импорта в следующей последовательности:

prog.py -> __init__.py -> main_menu.py -> file_menu.py -> main_menu.py -> [...]

Из нескольких поисков было предложил обновить импорт следующим образом:

file_menu.py

import tkinter as tk
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from prog.ui.menus import MainMenu

class FileMenu(tk.Menu):

    def __init__(self, master: 'MainMenu', **kwargs):
        super().__init__(master, **kwargs)
        self.add_command(label='Settings')

    [...]

Я прочитал документы Python и mypy документы по использованию, но я не понимаю, как использование этого условия разрешает цикл. Да, во время выполнения это работает, потому что оно оценивается как False, что является «операционным разрешением», но как оно не появляется при проверке типа:

Константа TYPE_CHECKING, определенная модулем набора, равна False во время выполнения, но True при проверке типа.

Я не очень разбираюсь в mypy, поэтому не могу понять, как после того, как условное выражение оценивается как True, проблема не появится снова. Что происходит по-разному между «временем выполнения» и «проверкой типов»? Означает ли процесс «проверки типа», что код не выполняется?

Примечания (не обязательно читать, но при желании необходимо указать дополнительные детали):

  • Это не проблема циклического импорта зависимостей, поэтому внедрение зависимостей не требуется

  • Это строго цикл, вызванный подсказкой типа для stati c анализ

  • Мне известны следующие параметры импорта (которые отлично работают):

    • Заменить from [...] import [...] на import [...]

    • Выполните импорт в MainMenu.__init__ и оставьте file_menu.py в покое

1 Ответ

0 голосов
/ 01 мая 2020

Означает ли процесс "проверки типа", что код не выполняется?

Да, точно. Средство проверки типов никогда не выполняет ваш код: вместо этого он анализирует его. Средства проверки типов реализованы практически так же, как и компиляторы, за исключением шага «генерировать байт-код / ​​сборка / машинный код».

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

Например, mypy в основном начинает с анализа кода вашего модуля за модулем, отслеживание каждого нового класса / нового типа, который определяется. Во время этого процесса, если mypy видит подсказку типа, используя тип, который еще не был определен, замените его типом заполнителя.

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

После этого mypy предполагает, что все оставшиеся заполнители являются просто недопустимыми типами, и сообщает об ошибке.


В отличие от этого, интерпретатор Python не может позволить себе повторный повторный анализ таких модулей. Он должен запускать каждый модуль, который видит, и многократный повторный запуск модулей может нарушить некоторый пользовательский код / ​​ожидания пользователя.

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

( Поэтому вместо этого mypy использует ваш импорт в качестве предложений , чтобы решить, в каком порядке анализировать модули. Например, если модуль A напрямую импортирует модуль B, мы, вероятно, сначала захотим проанализировать B. Но если A импортирует B позади if TYPE_CHECKING, возможно, неплохо бы ослабить порядок, если это поможет нам разорвать цикл.)

...