Quel est le problème avec les importations relatives en Python?

89

J'ai récemment mis à niveau les versions de pylint , un vérificateur de style Python populaire.

Il est devenu balistique dans tout mon code, en indiquant les endroits où j'importe des modules dans le même package, sans spécifier le chemin d'accès complet du package.

Le nouveau message d'erreur est W0403.

W0403: Importation relative% r, devrait être% r

Utilisé quand une importation relative au répertoire du paquet est détectée.


Exemple

Par exemple, si mes paquets sont structurés comme ceci:

/cake
  /__init__.py
  /icing.py
  /sponge.py
/drink

et dans le paquet d'éponge j'écris:

import icing

au lieu de

import cake.icing

Je vais avoir cette erreur.


Bien que je comprenne que tous les messages Pylint n’ont pas la même importance et que je n’ai pas peur de les rejeter, je ne comprends pas pourquoi une telle pratique est considérée comme une mauvaise idée.

J'espérais que quelqu'un pourrait expliquer les pièges afin de pouvoir améliorer mon style de codage plutôt que (comme je le fais actuellement) de désactiver cet avertissement apparemment faux.

Bizarre
la source

Réponses:

98

Le problème import icingest que vous ne savez pas s'il s'agit d'une importation absolue ou relative. icingpourrait un module dans le chemin de python, ou un paquet dans le module actuel. Ceci est assez gênant lorsqu'un paquet local porte le même nom qu'un paquet de bibliothèque standard Python.

Vous pouvez faire from __future__ import absolute_importce qui désactive complètement les importations relatives implicites. Il est décrit, y compris avec cette justification relative à l'ambiguïté, dans PEP 328 . Je pense que Python 3000 a complètement désactivé les importations relatives implicites.

Vous pouvez toujours faire des importations relatives, mais vous devez les faire explicitement, comme ceci:

from . import icing
Winston Ewert
la source
2
+1 en particulier pour la solution de compromis, qui est probablement la voie à suivre.
Oddthinking
2
Notez que vous pouvez également faire import .icingau lieu defrom . import icing
Jack
11
@ Jack en fait, je ne pense pas que vous puissiez le faire. De cette partie de PEP328 : Les importations relatives doivent toujours utiliser from <> import; import <>est toujours absolu. Bien sûr, les importations absolues peuvent utiliser from <> importen omettant les points principaux. La raison pour laquelle il import .fooest interdit est parce que , après , import XXX.YYY.ZZZpuis XXX.YYY.ZZZest utilisable dans une expression. Mais .moduleYn'est pas utilisable dans une expression.
A.Wan
47

Il y a quelques bonnes raisons:

  1. Les importations relatives se cassent facilement lorsque vous déplacez un module.

    Imaginez que vous ayez un foo.bar, un foo.bazet un bazmodule dans votre paquet. foo.barimportations foo.baz, mais en utilisant une importation relative.

    Maintenant, si vous deviez vous déplacer foo.barvers bar, votre module en importe soudainement un autre baz!

  2. Les importations relatives sont ambiguës. Même sans déplacer le barmodule dans l'exemple ci-dessus, un nouveau développeur venant dans votre projet pourrait être pardonné de ne pas se rendre compte que c'était bazvraiment foo.bazau lieu du bazpackage de niveau racine .

    Les importations absolues rendent explicite le module utilisé. Et comme import thisprêche, explicite vaut mieux qu'implicite.

  3. Python 3 a totalement désactivé les importations relatives implicites; les importations sont désormais toujours interprétées comme absolues, ce qui signifie que, dans l'exemple ci-dessus import baz, importera toujours le module de niveau supérieur. Vous devrez utiliser la syntaxe d'importation explicite à la place ( from . import baz).

    Porter l'exemple de Python 2 à 3 entraînerait donc des problèmes inattendus. Utiliser maintenant les importations absolues rendra votre code à l'épreuve du temps.

Martijn Pieters
la source
11
+1 pour # 2 et # 3. Mais # 1 doit être compensé par rapport à ce qui se produit lorsque tout le répertoire est déplacé (par exemple, poussé d'un niveau).
Oddthinking