Как я могу безопасно вводить методы проверки или аргументы в Python для разных файлов или пакетов, не создавая циклической зависимости?
Позвольте мне привести пример того, что < em>должен работать и работает в большинстве других строго типизированных языков (например, C/C++, Java и даже Hack), но, похоже, не имеет разумного решения в Python. Давайте возьмем пример очень распространенной структуры данных — графика.
Я хочу организовать структуру в несколько файлов, чтобы разделить задачи. В Python с утиной типизацией это может выглядеть так:
Код: Выделить всё
# Node.py
class Node:
def __init__(self):
self.edges = [] # Will be a list of Edge objects
def add_edge(self, edge):
self.edges.append(edge)
...
Код: Выделить всё
# Edge.py
class Edge:
def __init__(self, node, weight):
self.node = node
self.weight = weight
Но предположим, что я хочу реализовать проверку типов, чтобы гарантировать, что никто случайно не попытается передать целочисленный тип в add_edge или строку в "Edge()". ". Я бы напечатал это следующим образом:
Код: Выделить всё
# Node.py
from graph.Edge import Edge
class Node:
def __init__(self):
self.edges: List[Edge] = [] # Will be a list of Edge objects
def add_edge(self, edge: Edge):
self.edges.append(edge)
...
Код: Выделить всё
# Edge.py
from graph.Node import Node
class Edge:
def __init__(self, node: Node, weight: int):
self.node: Node = node
self.weight: int = weight
Наиболее распространенное решение, которое я видел, — это использовать if ТИП_ПРОВЕРКА. Но это приведет к тому, что проверка типов пропустит очень распространенный и критический случай, когда кто-то пытается использовать импорт для чего-то другого, кроме подсказки типа. Давайте рассмотрим пример:
Код: Выделить всё
# Node.py
if TYPE_CHECKING:
from graph.Edge import Edge
class Node:
def __init__(self):
self.edges: List[Edge] = [] # Will be a list of Edge objects
def add_edge(self, edge: Edge):
self.edges.append(edge)
Код: Выделить всё
# Node.py
if TYPE_CHECKING:
from graph.Edge import Edge
class Node:
def __init__(self):
self.edges: List[Edge] = [] # Will be a list of Edge objects
def add_edge(self, edge: Edge):
self.edges.append(edge)
def easy_add_edge(self, node: "Node", weight: int):
self.add_edges(Edge(node, weight))
Вы можете подумать: «Почему разработчик не написал тесты для этого метода», и вы будете правы, но это именно тот тип ошибки, который должна решать проверка типов! Для большинства разработчиков, работающих на других языках, проверка типов будет проверкой на наличие ошибок такого типа!
Не имея ни малейших знаний о внутренней работе подсказок типов в Python. системе решение обычно кажется очевидным — печатать, используя полные имена, и никогда не приближаться к переменной TYPE_CHECKING (мы могли бы даже написать правило проверки, чтобы отслеживать случаи, когда люди это делают). Но это та часть проблемы, где я уперся в стену, так как не могу понять, поддерживается ли это в Python или когда-либо будет поддерживаться.
Итак вопрос:
Как я могу безопасно проверять тип кода в Python таким образом, чтобы не вводить циклические зависимости, а также гарантировать, что разработчики не пытаются использовать необъявленные типы во время выполнения? Можно ли использовать полные имена, и если да, то как?
Примечание. Нецелесообразно размещать все, что может нуждаться в зависимости с указанием типа, в одном файле или даже тот же пакет. Разделение проблем на несколько пакетов — это то, что ожидается от любой команды инженеров практически для всех приложений промышленного масштаба. Я знаю, что есть способ сделать это, если все находится в одном файле, но я не могу преобразовать свое приложение Django из 200 тысяч строк в однофайловое приложение с плоской структурой.
Подробнее здесь: https://stackoverflow.com/questions/792 ... ar-imports
Мобильная версия