Estoy tratando de dividir mi gran clase en dos; bueno, básicamente en la clase "principal" y un mixin con funciones adicionales, así:
main.py
expediente:
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
mymixin.py
expediente:
class MyMixin(object):
def func2(self: Main, xxx): # <--- note the type hint
...
Ahora, aunque esto funciona bien, la sugerencia de tipo, por MyMixin.func2
supuesto, no puede funcionar. No puedo importar main.py
, porque obtendría una importación cíclica y sin la pista, mi editor (PyCharm) no puede decir qué self
es.
Estoy usando Python 3.4, dispuesto a pasar a 3.5 si hay una solución disponible allí.
¿Hay alguna forma de que pueda dividir mi clase en dos archivos y mantener todas las "conexiones" para que mi IDE todavía me ofrezca la finalización automática y todas las demás ventajas que se obtienen conociendo los tipos?
self
, ya que siempre será una subclase de la clase actual (y cualquier sistema de verificación de tipos debería poder resolver eso por sí solo). ¿Estáfunc2
intentando llamarfunc1
, que no está definido enMyMixin
? ¿Quizás debería ser (comoabstractmethod
, quizás)?class Main(MyMixin, SomeBaseClass)
para que los métodos de la clase más específica puedan anular los de la clase baseRespuestas:
Me temo que no existe una forma muy elegante de manejar los ciclos de importación en general. Sus opciones son rediseñar su código para eliminar la dependencia cíclica o, si no es factible, hacer algo como esto:
La
TYPE_CHECKING
constante siempre estáFalse
en tiempo de ejecución, por lo que la importación no se evaluará, pero mypy (y otras herramientas de verificación de tipos) evaluarán el contenido de ese bloque.También necesitamos convertir la
Main
anotación de tipo en una cadena, declarando efectivamente hacia adelante ya que elMain
símbolo no está disponible en tiempo de ejecución.Si está utilizando Python 3.7+, al menos podemos omitir tener que proporcionar una anotación de cadena explícita aprovechando PEP 563 :
La
from __future__ import annotations
importación hará que todos los tipos de sugerencias sean cadenas y omitirá su evaluación. Esto puede ayudar a que nuestro código aquí sea ligeramente más ergonómico.Dicho todo esto, el uso de mixins con mypy probablemente requerirá un poco más de estructura de la que tiene actualmente. Mypy recomienda un enfoque que es básicamente lo que
deceze
está describiendo - para crear un ABC que ambos susMain
yMyMixin
las clases heredan. No me sorprendería que terminaras necesitando hacer algo similar para hacer feliz al corrector de Pycharm.fuente
typing
, pero PyCharm también estaba bastante contentoif False:
.__init__
typing. TYPE_CHECKING
: python.org/dev/peps/pep-0484/#runtime-or-type-checkingPara las personas que luchan con las importaciones cíclicas al importar la clase solo para la verificación de tipo: es probable que desee utilizar una referencia directa (PEP 484 - Sugerencias de tipo):
Entonces en lugar de:
tú lo haces:
fuente
File -> Invalidate Caches
?if False:
tú también puedesfrom typing import TYPE_CHECKING
yif TYPE_CHECKING:
.El mayor problema es que, para empezar, tus tipos no están cuerdos.
MyMixin
hace una suposición codificada de que se mezclaráMain
, mientras que podría mezclarse con cualquier número de otras clases, en cuyo caso probablemente se rompería. Si su mixin está codificado para mezclarse en una clase específica, también puede escribir los métodos directamente en esa clase en lugar de separarlos.Para hacer esto correctamente con una escritura sana,
MyMixin
debe codificarse contra una interfaz o clase abstracta en el lenguaje de Python:fuente
Resulta que mi intento original también estuvo bastante cerca de la solución. Esto es lo que estoy usando actualmente:
Tenga en cuenta la
if False
declaración de importación interna que nunca se importa (pero IDE lo sabe de todos modos) y el uso de laMain
clase como cadena porque no se conoce en tiempo de ejecución.fuente
Creo que la forma perfecta debería ser importar todas las clases y dependencias en un archivo (como
__init__.py
) y luegofrom __init__ import *
en todos los demás archivos.En este caso eres
fuente
import *
, y aún así puede aprovechar este enfoque fácil