es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cambiando las sugerencias de tipo de Python para las subclases

Imagina el siguiente código para manejar gráficos utilizando una clase base y una subclase DAG:

class NodeBase:
    def some_node_func(self):
        pass

class GraphBase:
    def add(self, node: NodeBase):
        node.some_node_func()

class DirectedNode(NodeBase):
    def some_dag_func(self):
        pass

class DirectedGraph(GraphBase):
    def add(self, node: DirectedNode):
        node.some_node_func()
        node.some_dag_func()

Cuando intento usar este código con mypy, obtengo errores como:

error: El argumento 1 de "add" no es compatible con el supertipo "GraphBase";
el supertipo define el tipo de argumento como "NodeBase" [override]

Mi pregunta es superficialmente similar a https://stackoverflow.com/questions/68001249/python-how-to-handle-type-hinting-in-subclasses, pero en realidad necesito un comportamiento diferente de la función DirectedGraph.add que depende de la funcionalidad correspondiente de DirectedNode.

Sé que esto “viola el principio de sustitución de Liskov”, pero no necesito poder añadir instancias de DirectedNode a los gráficos no DAG.

¿Cómo puedo estructurar las cosas para que mypy no se queje? Me gustaría evitar deshabilitar las comprobaciones, si es posible.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para evitar el mensaje de error manteniendo el “type-hinting”, puedes utilizar tipos genéricos en la clase “GraphBase” para permitir que las subclases tengan sus propios tipos específicos de nodos. Aquí está una versión actualizada del código que logra esto:

    from typing import Generic, TypeVar
    
    N = TypeVar('N', bound=NodeBase)
    
    
    class NodeBase:
        def some_node_func(self):
            pass
    
    
    class GraphBase(Generic[N]):
        def add(self, node: N):
            node.some_node_func()
    
    
    class DirectedNode(NodeBase):
        def some_dag_func(self):
            pass
    
    
    class DirectedGraph(GraphBase[DirectedNode]):
        def add(self, node: DirectedNode):
            node.some_node_func()
            node.some_dag_func()
    

    En este código actualizado, utilizamos la clase “Generic” del módulo de “typing” de Python para hacer que “GraphBase” sea una clase genérica que acepte un parámetro de tipo “N”. Luego, utilizamos “TypeVar” para crear una variable “N” que tenga un límite de “NodeBase”, lo que significa que “N” debe ser una subclase de “NodeBase”.

    En la clase “GraphBase”, cambiamos el tipo de argumento del método “add” a “N” en lugar de “NodeBase”. Esto permite que “DirectedGraph” utilice “DirectedNode” como un argumento válido para el método “add”.

    Finalmente, al definir “DirectedGraph”, utilizamos la notación de corchetes cuadrados para especificar que el parámetro de tipo “N” para esta clase solo puede ser “DirectedNode”. Esto hace que el método “add” en la clase “DirectedGraph” pueda aceptar una instancia de “DirectedNode” sin causar un error por parte de “mypy”.

Comments are closed.