Skip to content

Instantly share code, notes, and snippets.

@kurtgn
Last active June 26, 2019 08:01
Show Gist options
  • Save kurtgn/482eb0033ad3122fd7108bd06bf0a402 to your computer and use it in GitHub Desktop.
Save kurtgn/482eb0033ad3122fd7108bd06bf0a402 to your computer and use it in GitHub Desktop.
mypy circular import problem
# circular import:
# node.py
from typing import Iterable
from nodeset import NodeSet
from typing import Iterable
from abstract_node import AbstractNode
from nodeset import NodeSet
class Node:
def __init__(self, name):
self.name = name
self.children = NodeSet()
def do_stuff(self) -> None:
print(f'node {self.name} does stuff')
def descendants(self) -> Iterable['Node']:
for child in self.children.all_nodes():
yield child
yield from child.descendants()
# nodeset.py
from typing import Iterable, List
from node import Node
class NodeSet:
def __init__(self):
self.nodes: List[Node] = []
def insert(self, node: Node) -> None:
self.nodes.append(node)
def all_nodes(self) -> Iterable[Node]:
for node in self.nodes:
yield node
# app.py
from node import Node
n = Node('a')
n.children.insert(Node('b'))
n.children.insert(Node('c'))
n.children.insert(Node('d'))
root = Node('r')
root.children.insert(n)
for d in root.descendants():
d.do_stuff()
# если запустить это:
Traceback (most recent call last):
File "/Users/1111/_projects/fasttrack/constructor/run_nodes.py", line 3, in <module>
from node import Node
File "/Users/1111/_projects/fasttrack/constructor/node.py", line 4, in <module>
from nodeset import NodeSet
File "/Users/1111/_projects/fasttrack/constructor/nodeset.py", line 3, in <module>
from node import Node
ImportError: cannot import name 'Node'
# ===========================================
# Решение проблемы: наследуем ноду от AbstractNode.
# NodeSet не обязан знать, что у него в списке лежат именно ноды. Пусть знает только об AbstractNode
# abstract_node.py
from abc import ABC, abstractmethod
from typing import Iterable
class AbstractNode(ABC):
@abstractmethod
def descendants(self) -> Iterable['AbstractNode']:
pass
@abstractmethod
def do_stuff(self) -> None:
pass
# node_set.py
# NodeSet не обязан знать, что у него в списке хранятся именно ноды. Это всего лишь коллекция.
# пусть знает только об интерфейсе
from typing import Iterable, List
from abstract_node import AbstractNode
class NodeSet:
def __init__(self):
self.nodes: List[AbstractNode] = []
def insert(self, node: AbstractNode) -> None:
self.nodes.append(node)
def all_nodes(self) -> Iterable[AbstractNode]:
for node in self.nodes:
yield node
def all_descendants(self) -> Iterable[AbstractNode]:
for node in self.nodes:
yield node
yield from node.descendants()
# node.py
from typing import Iterable
from abstract_node import AbstractNode
from nodeset import NodeSet
class Node(AbstractNode):
def __init__(self, name):
self.name = name
self.children = NodeSet()
def do_stuff(self) -> None:
print(f'node {self.name} does stuff')
def descendants(self) -> Iterable['AbstractNode']:
yield from self.children.all_descendants()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment