Необязательно или объединение с прямой ссылкой в ​​классе Python, неправильно помеченное mypy - PullRequest
0 голосов
/ 06 ноября 2019

Я определил класс TreeNode с аннотациями типов. Он принимает TreeNode или None для аргументов в __init__ и в некоторых методах класса. Полный код отлично работает во время тестирования, но когда я проверяю его с помощью mypy, он отображает 4 предупреждения. Я использую Python 3.7.4 и mypy версии 0.74.

Я пробовал это использовать оба ключевых слова: Optional ["TreeNode"] и Union ["TreeNode", None], но ошибки сохраняются. Обратите внимание, что кавычки вокруг TreeNode необходимы, поскольку это прямая ссылка на тип до его полного определения.

from typing import *

class TreeNode():
    def __init__(self, value,
                 parent : Optional["TreeNode"]=None,
                 left_child : Optional["TreeNode"]=None,
                 right_child : Optional["TreeNode"]=None):
        self.value = value
        self.parent = parent
        self.left_child = None
        self.right_child = None
        self.update_children(left_child, right_child)

    def get_parent(self):
        return self.parent

    def set_parent(self, other):
        self.parent = other

    def update_children(self,
                        left_child : Optional["TreeNode"] = None,
                        right_child : Optional["TreeNode"] = None):
        # update parents in children, if left or right not None.
        if left_child:
            self.left_child = left_child
            self.left_child.set_parent(self)
        if right_child:
            self.right_child = right_child
            self.right_child.set_parent(self)

    def depth(self):
        pass # full code omitted for brevity

Вот вывод mypy:

tree_node.py:25: error: Incompatible types in assignment (expression has type "TreeNode", variable has type "None")
tree_node.py:26: error: "None" has no attribute "set_parent"
tree_node.py:28: error: Incompatible types in assignment (expression has type "TreeNode", variable has type "None")
tree_node.py:29: error: "None" has no attribute "set_parent"

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

        if left_child is not None:
            self.left_child = left_child
            self.left_child.set_parent(self)
        if right_child is not None:
            self.right_child = right_child
            self.right_child.set_parent(self)

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

1 Ответ

0 голосов
/ 06 ноября 2019

Проблема связана с тем, как вы устанавливаете левый и правый дочерние элементы в конструкторе, в частности, эти две строки:

self.left_child = None
self.right_child = None

Поскольку этим полям присваиваются значения Nonemypy в итоге просто консервативно делает вывод, что типы этих полей в точности равны None. И Optional[TreeNode] не является подтипом None, поэтому назначение в update_children заканчивается неудачей.

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

Обходной путь в этом случае - просто явно дать этим двум полям подсказку типа. Например, если вы используете Python 3.6+, вы можете использовать переменные аннотации:

self.left_child: Optional[TreeNode] = None
self.right_child: Optional[TreeNode] = None

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

self.left_child = None   # type: Optional[TreeNode]
self.right_child = None  # type: Optional[TreeNode]
...