Vous pouvez mettre du code dans __init__.py


Le fichier __init__.py ne sert pas qu’à déclarer un dossier comme un package importable. C’est aussi le code exécuté automatiquement, une seule fois, quand on importe le module.

Du coup vous pouvez mettre dedans tout code Python que vous souhaitez lancer à l’import. On y voit souvent :

  • Initialisation du cache.
  • Constantes comme __version__.
  • Import d’autres modules pour les mettre dans l’espace de nom courant.
  • Check de dépendances.
  • Aliasing.
  • Monkey patching and hacks tout moches qui sont là temporairement pour 6 ans.

Je vous déconseille de mettre trop de code dans le fichier __init__.py, notamment du code métier. C’est une mauvaise habitude que l’on peut voir dans le code de Django par exemple. Car ça veut dire que l’import du package déclenche ce code, qui lui-même importe d’autres modules, qui déclenche d’autres codes, etc. Cela donne des effets de bord à l’import, alors que l’import d’un simple package est quelque chose que l’on veut généralement ne pas avoir beaucoup d’effets.

Dans Django par exemple, c’est ce qui fait que beaucoup de modules lèvent une exception à l’import :

django.core.exceptions.ImproperlyConfigured: Requested setting X but settings are not configured.

Alors qu’ils n’ont pas du tout besoin des settings pour fonctionner.

Si vous avez besoin que votre classe Bidule soit directement importable dans votre package machin, faites dans machin/__init__.py :

from .module_qui_contient_bidule import Bidule

Du coup vous pourrez faire n’importe où ailleurs:

from machin import Bidule

C’est bien plus propre que de mettre tout le code de Bidule dans le __init__.py.

12 thoughts on “Vous pouvez mettre du code dans __init__.py

  • martin

    Et je reviens aussi avec mes fichiers .pth

    Vous pouvez aussi y mettre du code qui s’exécute automatiquement:

    Dans un fichier truc.pth:


    import sys; exec(''.join(f) if __name__ == 'site' else '', {})
    if vous(voulez):
    vous pouvez exécuter n'importe quoi ici ici

  • foxmask

    avec Django je me sers des __init__.py dans ds dossier models, forms’pour avoir des modèles distincts par service.c’est une connerie ?

  • Sam Post author

    J’ai pas compris foxmask. Poste une arbo et le contenu des fichiers.

  • residante

    Globalement ça sert à exposer l’api publique d’un projet. En tout cas c’est comme ça que je l’utilise :)

    Sinon c’est quoi l’Aliasing dans ce contexte ?

  • Sam Post author

    Par exemple :

    from bidule import Truc
    OldTrucName = Truc

    Ce qui permet de faire des versions de transition quand on fait du deprecated.

  • kontre

    J’ai eu un instant d’incompréhension aussi : c’est aliasing comme un alias, pas comme le lissage des bordures dans les affichages 3D.

    Ca peut servir aussi pour éviter les from bidule.machin import Machin en ayant uniquement from bidule import Machin (avec from .machin import Machin dans le __init__.py).

  • foxmask

    @sam
    je n’ai pas de forms.py ni de models.py mais un dossier forms et un dossier models et j’ai mis dans le __init__.py de chacun, ce qui est commun à toute mon appli.

    donc partant de ton commentaire :
    > Je vous déconseille de mettre trop de code dans le fichier __init__.py, notamment du code métier.
    je me demandais si j’avais bien fait de le faire comme ça.

  • Sam Post author

    Nan, c’est pas une bonne idée. Met ça dans un fichier base.py et importe juste le nécessaire dans __init__ sinon un jour quelqu’un va vouloir importer forms.un_truc, et ça va lancer le code du __init__ alors que lui il voulait juste un_truc.

  • Le Vengeur Masqué

    Hello,

    L’ordre des import dans __init__.py peut-il avoir une importance ?

    Dans certains cas j’ai des erreurs dans mon code, je ne comprends pas très bien pourquoi.

  • Sam Post author

    Oui, puisque les imports exécutes le code des modules importés.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.