Maison  >  Article  >  développement back-end  >  Différentes façons de corriger les importations circulaires en Python

Différentes façons de corriger les importations circulaires en Python

Linda Hamilton
Linda Hamiltonoriginal
2024-11-05 02:21:01726parcourir

Avez-vous déjà rencontré des importations circulaires en Python ? Eh bien, c'est une odeur de code très courante qui indique que quelque chose ne va pas avec la conception ou la structure.

Exemple d'importation circulaire

Comment se produit l'importation circulaire ? Cette erreur d'importation se produit généralement lorsque deux modules ou plus dépendant les uns des autres tentent d'importer avant de s'initialiser complètement.

Disons que nous avons deux modules : module_1.py et module_2.py.

# module_1.py
from module_2 import ModY
class ModX:
    mody_obj = ModY()
# module_2.py
from module_1 import ModX
class ModY:
    modx_obj = ModX()

Dans les extraits de code ci-dessus, module_1 et module_2 dépendent mutuellement l'un de l'autre.

L'initialisation de mody_obj dans module_1 dépend du module_2 et l'initialisation de modx_obj dans module_2 dépend du module_1.

C'est ce que nous appelons une dépendance circulaire. Les deux modules resteront bloqués dans les boucles d'importation lors de la tentative de chargement mutuel.

Si nous exécutons module_1.py, nous obtiendrons le traçage suivant.

Traceback (most recent call last):
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
  File "module_2.py", line 1, in <module>
    from module_1 import ModX
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)

Cette erreur explique la situation de l'importation circulaire. Lorsque le programme a tenté d'importer ModY depuis module_2, à ce moment-là, module_2 n'était pas complètement initialisé (en raison d'une autre instruction d'importation qui tente d'importer ModX depuis module_1).

Comment réparer les importations circulaires en Python ? Il existe différentes façons de se débarrasser des importations circulaires en Python.

Corriger les importations circulaires en Python

Déplacer le code dans un fichier commun

Nous pouvons déplacer le code dans un fichier commun pour éviter les erreurs d'importation, puis essayer d'importer les modules à partir de ce fichier.

# main.py ----> common file
class ModX:
    pass

class ModY:
    pass

Dans l'extrait de code ci-dessus, nous avons déplacé les classes ModX et ModY dans un fichier commun (main.py).

# module_1.py
from main import ModY

class Mod_X:
    mody_obj = ModY()
# module_2.py
from main import ModX

class Mod_Y:
    modx_obj = ModX()

Maintenant, module_1 et module_2 importent les classes de main, ce qui corrige la situation d'importation circulaire.

Il y a un problème avec cette approche, parfois la base de code est si volumineuse qu'il devient risqué de déplacer le code dans un autre fichier.

Déplacer l'import à la fin du module

On peut décaler l'instruction import à la fin du module. Cela donnera le temps d'initialiser complètement le module avant d'importer un autre module.

# module_1.py
class ModX:
   pass

from module_2 import ModY

class Mod_X:
   mody_obj = ModY()
# module_2.py
class ModY:
   pass

from module_1 import ModX

Module d'importation dans la portée de la classe/fonction

L'importation de modules dans la portée de la classe ou de la fonction peut éviter les importations circulaires. Cela permet au module d'être importé uniquement lorsque la classe ou la fonction est invoquée. C'est pertinent lorsque nous voulons minimiser l'utilisation de la mémoire.

# module_1.py
class ModX:
  pass

class Mod_X:
   from module_2 import ModY
   mody_obj = ModY()
# module_2.py
class ModY:
   pass

class Mod_Y:
   from module_1 import ModX
   modx_obj = ModX()

Nous avons déplacé les instructions d'importation dans les classes Mod_X et Mod_Y respectivement dans module_1 et module_2.

Si nous exécutons module_1 ou module_2, nous n'obtiendrons pas d'erreur d'importation circulaire. Mais cette approche rend la classe accessible uniquement dans la portée de la classe, nous ne pouvons donc pas exploiter l’importation globalement.

Utilisation du nom/alias du module

Utiliser le nom du module ou simplement un alias comme celui-ci résout le problème. Cela permet aux deux modules de se charger complètement en différant la dépendance circulaire jusqu'à l'exécution.

# module_1.py
from module_2 import ModY
class ModX:
    mody_obj = ModY()
# module_2.py
from module_1 import ModX
class ModY:
    modx_obj = ModX()

Utilisation de la bibliothèque importlib

On peut également utiliser la bibliothèque importlib pour importer les modules dynamiquement.

Traceback (most recent call last):
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
  File "module_2.py", line 1, in <module>
    from module_1 import ModX
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
# main.py ----> common file
class ModX:
    pass

class ModY:
    pass

Importations circulaires dans les packages Python

Habituellement, les importations circulaires proviennent de modules au sein du même package. Dans les projets complexes, la structure des répertoires est également complexe, avec des packages dans les packages.

Ces packages et sous-packages contiennent des fichiers __init__.py pour faciliter l'accès aux modules. C'est là que surgissent parfois involontairement des dépendances circulaires entre les modules.

Nous avons la structure de répertoires suivante.

# module_1.py
from main import ModY

class Mod_X:
    mody_obj = ModY()

Nous avons un package mainpkg et un fichier main.py. Nous avons deux sous-packages modpkg_x et modpkg_y dans mainpkg.

Voici à quoi ressemble chaque fichier Python dans modpkg_x et modpkg_y.

mainpkg/modpkg_x/__init__.py

# module_2.py
from main import ModX

class Mod_Y:
    modx_obj = ModX()

Ce fichier importe les deux classes (ModX et ModA) de module_1 et module_1_1.

mainpkg/modpkg_x/module_1.py

# module_1.py
class ModX:
   pass

from module_2 import ModY

class Mod_X:
   mody_obj = ModY()

Le module_1 importe une classe ModY du module_2.

mainpkg/modpkg_x/module_1_1.py

# module_2.py
class ModY:
   pass

from module_1 import ModX

Le module_1_1 n'importe rien. Il ne dépend d'aucun module.

mainpkg/modpkg_y/__init__.py

# module_1.py
class ModX:
  pass

class Mod_X:
   from module_2 import ModY
   mody_obj = ModY()

Ce fichier importe la classe ModY du module_2.

mainpkg/modpkg_y/module_2.py

# module_2.py
class ModY:
   pass

class Mod_Y:
   from module_1 import ModX
   modx_obj = ModX()

Le module_2 importe une classe ModA du module_1_1.

Nous avons le code suivant dans le fichier main.py.

root_dir/main.py

# module_1.py
import module_2 as m2

class ModX:
    def __init__(self):
        self.mody_obj = m2.ModY()

Le fichier principal importe une classe ModY du module_2. Ce fichier dépend du module_2.

Si nous visualisons le cycle d'importation ici, cela ressemblerait à ce qui suit en ignorant les fichiers __init__.py dans modpkg_x et modpkg_y.

Different Ways to Fix Circular Imports in Python

On voit que le fichier principal dépend du module_2, le module_1 dépend également du module_2 et le module_2 dépend du module_1_1. Il n'y a pas de cycle d'importation.

Mais vous savez, les modules dépendent de leur fichier __init__.py, donc le fichier __init__.py s'initialise en premier et les modules sont réimportés.

Different Ways to Fix Circular Imports in Python

Voici à quoi ressemble le cycle d'importation maintenant.

Different Ways to Fix Circular Imports in Python

Cela rendait module_1_1 dépendant de module_1, qui est une fausse dépendance.

Si tel est le cas, videz les fichiers __init__.py des sous-packages et l'utilisation d'un fichier __init__.py distinct peut aider en centralisant les importations au niveau du package.

# module_1.py
from module_2 import ModY
class ModX:
    mody_obj = ModY()

Dans cette structure, nous avons ajouté un autre sous-paquet subpkg dans mainpkg.

mainpkg/subpkg/__init__.py

# module_2.py
from module_1 import ModX
class ModY:
    modx_obj = ModX()

Cela permettra aux modules internes d'importer à partir d'une source unique, réduisant ainsi le besoin d'importations croisées.

Nous pouvons maintenant mettre à jour l'instruction d'importation dans le fichier main.py.

root_dir/main.py

Traceback (most recent call last):
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
  File "module_2.py", line 1, in <module>
    from module_1 import ModX
  File "module_1.py", line 1, in <module>
    from module_2 import ModY
ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)

Cela résout le problème de dépendance circulaire entre les modules au sein d'un même package.

Conclusion

La dépendance circulaire ou l'importation en Python est une odeur de code qui est une indication d'une restructuration et d'une refactorisation sérieuses du code.

Vous pouvez essayer l'une des méthodes mentionnées ci-dessus pour éviter les dépendances circulaires en Python.


?D'autres articles qui pourraient vous intéresser si celui-ci vous a plu

✅Héritage de modèles dans Flask avec exemple.

✅Différence entre exec() et eval() avec exemples.

✅Comprendre l'utilisation du mot-clé global en Python.

✅Conseils sur le type Python : fonctions, valeurs de retour, variable.

✅Pourquoi la barre oblique et l'astérisque sont utilisés dans la définition des fonctions.

✅Comment le taux d'apprentissage affecte-t-il les modèles ML et DL ?


C'est tout pour le moment.

Continuez à coder✌✌.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn