Пакеты подсказок Python без создания циклического импортаPython

Программы на Python
Ответить
Anonymous
 Пакеты подсказок Python без создания циклического импорта

Сообщение Anonymous »

Я рыскал в Интернете, пытаясь найти решение этой проблемы, но так и не нашел ответа, несмотря на то, что это похоже на очень распространенный вариант использования:
Как я могу безопасно вводить методы проверки или аргументы в 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
Теперь в приведенном выше примере ни одному объекту не нужно импортировать другой, поэтому все работает. Во время выполнения другой класс, например Graph, может отвечать за создание узлов и ребер, а также добавление ребер к узлам. Циклической зависимости пока нет.
Но предположим, что я хочу реализовать проверку типов, чтобы гарантировать, что никто случайно не попытается передать целочисленный тип в 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)
Это предотвращает циклический импорт, и при запускеpyright илиpyre ошибок не будет, поскольку ввод верен. Но теперь предположим, что кто-то добавляет в класс Node новый метод, подобный следующему:

Код: Выделить всё

# 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))
Затем разработчик запускает Pyright, который подтверждает, что в этом методе нет ошибок типа. Загоняют в производство, и вдруг все ломается. Поскольку импорт для Edge был внутри, если TYPE_CHECKING, который имеет значение False во время выполнения, Python не сможет разрешить self.add_edges(Edge(node, Weight)) и выдать исключение. Но программа проверки типов не уловила это, потому что при ее запуске TYPE_CHECKING было True!
Вы можете подумать: «Почему разработчик не написал тесты для этого метода», и вы будете правы, но это именно тот тип ошибки, который должна решать проверка типов! Для большинства разработчиков, работающих на других языках, проверка типов будет проверкой на наличие ошибок такого типа!
Не имея ни малейших знаний о внутренней работе подсказок типов в Python. системе решение обычно кажется очевидным — печатать, используя полные имена, и никогда не приближаться к переменной TYPE_CHECKING (мы могли бы даже написать правило проверки, чтобы отслеживать случаи, когда люди это делают). Но это та часть проблемы, где я уперся в стену, так как не могу понять, поддерживается ли это в Python или когда-либо будет поддерживаться.
Итак вопрос:
Как я могу безопасно проверять тип кода в Python таким образом, чтобы не вводить циклические зависимости, а также гарантировать, что разработчики не пытаются использовать необъявленные типы во время выполнения? Можно ли использовать полные имена, и если да, то как?
Примечание. Нецелесообразно размещать все, что может нуждаться в зависимости с указанием типа, в одном файле или даже тот же пакет. Разделение проблем на несколько пакетов — это то, что ожидается от любой команды инженеров практически для всех приложений промышленного масштаба. Я знаю, что есть способ сделать это, если все находится в одном файле, но я не могу преобразовать свое приложение Django из 200 тысяч строк в однофайловое приложение с плоской структурой.

Подробнее здесь: https://stackoverflow.com/questions/792 ... ar-imports
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»