Comportement import python 3.7.2/ python 3.11.3

Résolu

Bonjour,

J'ai un programme principal test.py:

import sys
import os

sys.path.append(os.getcwd() + r'\data')
from test_lib import *

et une bibliothèque de fonctions test_lib.py :

def get_os():

    if sys.platform in ('win32','linux'):
        font_default = ('Segoe UI', 9)
        key_codes = [13, 13]
        return font_default,0,0,0,0, key_codes
    else:
        font_default = ('Segoe UI', 10)
        key_codes = [1275068419, 603979789]
        return font_default,25,-0,15,-2, key_codes

Ceci fonctionne parfaitement en version 3.7.2 car sys utilisé dans get_os()

est importé dans test.py, et après vient l'import de test_lib

Par contre, en version 3.11.3, j'ai une erreur:

NameError: name 'sys' is not defined  à la ligne if sys.platform....

J'ai résolu ceci en ajoutant import sys dans test_lib.py

Quelque chose aurait-il changé entre les 2 versions ?

Merci d'avance pour vos réponses


Windows / Edge 121.0.0.0

7 réponses

  1. Salut.

    Je me doutais que ce qui diffère est :

    https://docs.python.org/3/tutorial/modules.html#tut-pkg-import-star

    Je n'ai pas la 3.11 mais comme plus récent la 3.9 et en regardant le code source du module datetime des deux versions, en 3.9 il y a :

    __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo",
               "MINYEAR", "MAXYEAR")

    Le import * c'est le mal ! les seules fois où je m'en sers est pour l'import de constantes, et encore tout dépend du nombre de constantes...

    1. Salut Phil,

      Je suis étonné qu'avec Python 3.7 ça pouvait fonctionner sans importer sys...

      1. Holà Diablo,

        Ben si : on importe sys dans test.py puis on importe test_lib plus bas

        Normalement, importer test_lib, ou écrire à la place le code contenu dedans

        c'est la même chose, c'est comme si on écrivait cà:

        import sys
        import os
        
        sys.path.append(os.getcwd() + r'\data')
        #from test_lib import *
        
        def get_os():
        
            if sys.platform in ('win32','linux'):
                font_default = ('Segoe UI', 9)
                key_codes = [13, 13]
                return font_default,0,0,0,0, key_codes
            else:
                font_default = ('Segoe UI', 10)
                key_codes = [1275068419, 603979789]
                return font_default,25,-0,15,-2, key_codes

        non ?

        1. Ha bah non.

          Tu dois importer sys (ou autre) dans chaque module qui l'utilise. 

      2. Salut,

        Diablo76 a raison.

        Les modules sont des espaces de noms, le module importé n'a pas connaissance de ce qu'il se passe dans celui qui l'importe.

        Chez moi, en 3.7 c'est bien une erreur disant NameError: name 'sys' is not defined, cela a toujours été le cas, même en python 2 dont l'erreur est NameError: global name 'sys' is not defined.

        1. Bizarre, chez moi, ça marche ...

          J'ai même plusieurs programmes qui marchent comme ça:

          cad3_common.py:

          import os
          import sys
          from copy import *
          import pickle
          import re

          cad3_modeller.py:

          from cad_3_common import *
          
          def get_file_objects(filename):
          	''' Opens the solid file and records the objects '''
          
              with open(filename,'rb') as fo:
                  G.objects_list = pickle.load(fo)
                  G.points_model = pickle.load(fo)
                  G.faces = pickle.load(fo)
                  G.edges = pickle.load(fo)

          cad3_modeller utilise pickle qui est importé dans cad3_common

          cad3_common est importé dans cad3_modeller

          1. Eureka !

            dans test_lib.py, il y a aussi 

            from datetime import *

            et je pense que sys est inclus dedans, je viens de faire un test, et c'est bien ça:

            # -*- coding:Utf-8 -*-
            
            from datetime import *
            
            def get_os():
            
                if sys.platform in ('win32','linux'):
                    font_default = ('Segoe UI', 9)
                    key_codes = [13, 13]
                    return font_default,0,0,0,0, key_codes
                else:
                    font_default = ('Segoe UI', 10)
                    key_codes = [1275068419, 603979789]
                    return font_default,25,-0,15,-2, key_codes

            Il y avait plusieurs imports, et je les ai éliminés un par un pour tester

            Et donc je pense qu'en 3.11.3, ils ont resserré les boulons 

            1. Ok, on comprend mieux, la logique est que chaque module importe ses propres dépendances, car la chose que l'on a besoin de connaitre de datetime, ce sont ses méthodes et attributs.

              Si demain datetime n'utilise plus sys.platform pour détecter l'os, ça posera un problème.

              Je ne sais pas si je suis clair dans ma réponse.

            2. Contributeur

              Ce qui confirme qu'il est préférable d'éviter "import *".

            3. Modérateur
              @yg_be

              Pour préciser cette réponse, écrire un import du style

              from foo import *

              présente deux inconvénients qu'il faut bien distinguer.

              1) On ne sait pas ce qui est importé depuis le module foo, ce qui nuit à la lisibilité du code. On importe d'ailleurs probablement par la même occasion beaucoup de symboles qui n'ont aucune raison de l'être, voire qui peuvent entrer en collision avec d'autres symboles définis plus loin dans le code.

              2) En particulier, on importe non seulement les symboles définis dans foo, mais aussi tout symboles définis dans foo, ce qui inclue tous les symboles importés dans foo (qui eux, proviennent modules). Ainsi, si foo.py contient :

              from bar import Bar
              
              class Foo(Bar):
                  pass

              ... alors :

              from foo import *

              ... importe non seulement Foo, mais aussi Bar.

              C'est là que les ennuis commencent, car on peut croire à tort que foo fournit Bar,  qui est en réalité fourni par le module bar. Là où ça devient problématique, c'est si l'implémentation de foo évolue. En effet, rien ne garantit que foo continuera de dépendre de bar, et donc rien ne garantit qu'il pourra continuer d'exposer par la suite Bar. 

              Ainsi si un module a besoin de Foo et Bar, il ne faut ni écrire :

              from foo import *

              ... ni (même si c'est mieux) :

              from foo import Foo, Bar

              ... mais :

              from foo import Foo
              from bar import Bar

              Bonne chance

          2. En tout cas, ce qui est sûr, c'est qu'en 3.7.2, ça marche comme ça ...

            1. Oui et je me souviens que l'on avait eu ce même genre de discutions avec Tkinter et le module RegEx (re).