J'essaye de diviser mon immense classe en deux; Eh bien, fondamentalement, dans la classe "principale" et un mixin avec des fonctions supplémentaires, comme suit:
# main.py
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
# mymixin.py
class MyMixin(object):
def func2(self: Main, xxx): # <--- note the type hint
...
Maintenant, alors que cela fonctionne très bien, l'indicateur de type dans MyMixin.func2 ne peut bien sûr pas fonctionner. Je ne peux pas importer main.py, car j'obtiendrais une importation cyclique et sans indice, mon éditeur (PyCharm) ne peut pas dire ce que self
est.
Utilisation de Python 3.4, souhaitant passer à la version 3.5 si une solution est disponible.
Est-il possible de diviser ma classe en deux fichiers et de conserver toutes les "connexions" de sorte que mon IDE m'offre toujours l'auto-complétion et tous les autres avantages qui en découlent en connaissant les types?
J'ai bien peur qu'il n'y ait pas de manière extrêmement élégante de gérer les cycles d'importation. Vous avez le choix entre redéfinir votre code pour supprimer la dépendance cyclique ou, si cela n’est pas réalisable, procédez comme suit:
# some_file.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from main import Main
class MyObject(object):
def func2(self, some_param: 'Main'):
...
La constante TYPE_CHECKING
étant toujours False
au moment de l'exécution, l'importation ne sera pas évaluée, mais mypy (et d'autres outils de vérification du type) évaluera le contenu de ce bloc.
Nous avons également besoin de transformer l'annotation de type Main
en une chaîne, de manière efficace à la déclarer en aval puisque le symbole Main
n'est pas disponible au moment de l'exécution.
Si vous utilisez Python 3.7+, nous pouvons au moins éviter de devoir fournir une annotation de chaîne explicite en tirant parti de PEP 563 :
# some_file.py
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from main import Main
class MyObject(object):
# Hooray, cleaner annotations!
def func2(self, some_param: Main):
...
L'importation from __future__ import annotations
transformera les indicateurs de type all en chaînes et ignorera leur évaluation. Cela peut contribuer à rendre notre code légèrement plus ergonomique.
Cela dit, l'utilisation de mixins avec mypy nécessitera probablement un peu plus de structure que ce que vous avez actuellement. Mypy recommande une approche c'est en gros ce que décrit deceze
- pour créer un ABC dont vos classes Main
et MyMixin
héritent. Je ne serais pas surpris si vous deviez faire quelque chose de similaire pour rendre le vérificateur de Pycharm heureux.
Le plus gros problème est que vos types ne sont pas sains d’esprit pour commencer. MyMixin
fait l'hypothèse codée en dur qu'il sera mélangé à Main
, alors qu'il pourrait être mélangé à un nombre quelconque d'autres classes, auquel cas il serait probablement cassé. Si votre mixin est codé en dur pour être mélangé dans une classe spécifique, vous pouvez également écrire les méthodes directement dans cette classe au lieu de les séparer.
Pour faire cela correctement avec une frappe correcte, MyMixin
devrait être codé contre une classe interface , ou une classe abstraite en langage Python:
import abc
class MixinDependencyInterface(abc.ABC):
@abc.abstractmethod
def foo(self):
pass
class MyMixin:
def func2(self: MixinDependencyInterface, xxx):
self.foo() # ← mixin only depends on the interface
class Main(MixinDependencyInterface, MyMixin):
def foo(self):
print('bar')
Il s'avère que ma tentative initiale était également proche de la solution. C'est ce que j'utilise actuellement:
# main.py
import mymixin.py
class Main(object, MyMixin):
def func1(self, xxx):
...
# mymixin.py
if False:
from main import Main
class MyMixin(object):
def func2(self: 'Main', xxx): # <--- note the type hint
...
Notez l'import dans l'instruction if False
qui n'est jamais importée (mais IDE le sait de toute façon) et en utilisant la classe Main
en tant que chaîne car elle n'est pas connue à l'exécution.
Je pense que le moyen idéal devrait être d'importer toutes les classes et dépendances dans un fichier (comme __init__.py
) puis from __init__ import *
dans tous les autres fichiers.
Dans ce cas, vous êtes