7. Noms et espaces de nommage#
7.1. Vue d’ensemble#
Ce cours porte entièrement sur les noms de variables, sur la façon dont ils peuvent être utilisés et sur la manière dont ils sont interprétés par l’interpréteur Python.
Cela peut sembler un peu ennuyeux, mais le modèle qu’a adopté Python pour gérer les noms est élégant et intéressant.
De plus, vous vous épargnerez de nombreuses heures de débogage si vous comprenez bien le fonctionnement des noms en Python.
7.2. Noms de variables en Python#
Considérons l’instruction Python
x = 42
Nous savons désormais que lorsque cette instruction est exécutée, Python crée un objet de type int dans la mémoire de votre ordinateur, contenant
la valeur
42certains attributs associés
Mais qu’est-ce que x lui-même ?
En Python, x est appelé un nom, et l’instruction x = 42 lie le nom x à l’objet entier dont nous venons de parler.
En interne, ce processus de liaison des noms aux objets est implémenté sous la forme d’un dictionnaire — nous y reviendrons dans un instant.
Il n’y a aucun problème à lier deux noms ou plus à un même objet, quel que soit cet objet
def f(string): # Crée une fonction appelée f
print(string) # qui affiche toute chaîne qui lui est passée
g = f
id(g) == id(f)
True
g('test')
test
Lors de la première étape, un objet fonction est créé, et le nom f lui est lié.
Après avoir lié le nom g au même objet, nous pouvons l’utiliser partout où nous utiliserions f.
Que se passe-t-il lorsque le nombre de noms liés à un objet tombe à zéro ?
Voici un exemple de cette situation, où le nom x est d’abord lié à un objet puis relié à un autre
x = 'foo'
id(x)
x = 'bar'
id(x)
140468131677408
Dans ce cas, après avoir relié x à 'bar', aucun nom n’est lié au premier objet 'foo'.
Cela entraîne la récupération de 'foo' par le ramasse-miettes.
Autrement dit, l’emplacement mémoire qui stocke cet objet est libéré et rendu au système d’exploitation.
Le fonctionnement des ramasse-miettes est encore un domaine de recherche actif en informatique.
Vous pouvez en lire davantage sur le fonctionnement des ramasse-miettes si cela vous intéresse.
7.3. Espaces de nommage#
Rappelons que l’instruction
x = 42
lie le nom x à l’objet entier situé à droite.
Nous avons également mentionné que ce processus de liaison de x au bon objet est implémenté sous la forme d’un dictionnaire.
Ce dictionnaire est appelé un espace de nommage.
Définition
Un espace de nommage est une table de correspondance entre des noms et des objets en mémoire.
Python utilise plusieurs espaces de nommage, qu’il crée à la volée selon les besoins.
Par exemple, chaque fois que nous importons un module, Python crée un espace de nommage pour ce module.
Pour voir cela en action, supposons que nous écrivions un script mathfoo.py composé d’une seule ligne
%%file mathfoo.py
pi = 'foobar'
Writing mathfoo.py
Maintenant, nous démarrons l’interpréteur Python et l’importons
import mathfoo
Ensuite, importons le module math de la bibliothèque standard
import math
Ces deux modules possèdent un attribut appelé pi
math.pi
3.141592653589793
mathfoo.pi
'foobar'
Ces deux liaisons différentes de pi existent dans des espaces de nommage différents, chacun implémenté sous la forme d’un dictionnaire.
Si vous le souhaitez, vous pouvez consulter directement le dictionnaire, en utilisant module_name.__dict__.
import math
math.__dict__.items()
dict_items([('__name__', 'math'), ('__doc__', 'This module provides access to the mathematical functions\ndefined by the C standard.'), ('__package__', ''), ('__loader__', <_frozen_importlib_external.ExtensionFileLoader object at 0x7fc14ab2da30>), ('__spec__', ModuleSpec(name='math', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x7fc14ab2da30>, origin='/home/runner/miniconda3/envs/quantecon/lib/python3.13/lib-dynload/math.cpython-313-x86_64-linux-gnu.so')), ('acos', <built-in function acos>), ('acosh', <built-in function acosh>), ('asin', <built-in function asin>), ('asinh', <built-in function asinh>), ('atan', <built-in function atan>), ('atan2', <built-in function atan2>), ('atanh', <built-in function atanh>), ('cbrt', <built-in function cbrt>), ('ceil', <built-in function ceil>), ('copysign', <built-in function copysign>), ('cos', <built-in function cos>), ('cosh', <built-in function cosh>), ('degrees', <built-in function degrees>), ('dist', <built-in function dist>), ('erf', <built-in function erf>), ('erfc', <built-in function erfc>), ('exp', <built-in function exp>), ('exp2', <built-in function exp2>), ('expm1', <built-in function expm1>), ('fabs', <built-in function fabs>), ('factorial', <built-in function factorial>), ('floor', <built-in function floor>), ('fma', <built-in function fma>), ('fmod', <built-in function fmod>), ('frexp', <built-in function frexp>), ('fsum', <built-in function fsum>), ('gamma', <built-in function gamma>), ('gcd', <built-in function gcd>), ('hypot', <built-in function hypot>), ('isclose', <built-in function isclose>), ('isfinite', <built-in function isfinite>), ('isinf', <built-in function isinf>), ('isnan', <built-in function isnan>), ('isqrt', <built-in function isqrt>), ('lcm', <built-in function lcm>), ('ldexp', <built-in function ldexp>), ('lgamma', <built-in function lgamma>), ('log', <built-in function log>), ('log1p', <built-in function log1p>), ('log10', <built-in function log10>), ('log2', <built-in function log2>), ('modf', <built-in function modf>), ('pow', <built-in function pow>), ('radians', <built-in function radians>), ('remainder', <built-in function remainder>), ('sin', <built-in function sin>), ('sinh', <built-in function sinh>), ('sqrt', <built-in function sqrt>), ('tan', <built-in function tan>), ('tanh', <built-in function tanh>), ('sumprod', <built-in function sumprod>), ('trunc', <built-in function trunc>), ('prod', <built-in function prod>), ('perm', <built-in function perm>), ('comb', <built-in function comb>), ('nextafter', <built-in function nextafter>), ('ulp', <built-in function ulp>), ('__file__', '/home/runner/miniconda3/envs/quantecon/lib/python3.13/lib-dynload/math.cpython-313-x86_64-linux-gnu.so'), ('pi', 3.141592653589793), ('e', 2.718281828459045), ('tau', 6.283185307179586), ('inf', inf), ('nan', nan)])
import mathfoo
mathfoo.__dict__
{'__name__': 'mathfoo',
'__doc__': None,
'__package__': '',
'__loader__': <_frozen_importlib_external.SourceFileLoader at 0x7fc14488de50>,
'__spec__': ModuleSpec(name='mathfoo', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7fc14488de50>, origin='/home/runner/work/lecture-python-programming.fr/lecture-python-programming.fr/lectures/mathfoo.py'),
'__file__': '/home/runner/work/lecture-python-programming.fr/lecture-python-programming.fr/lectures/mathfoo.py',
'__cached__': '/home/runner/work/lecture-python-programming.fr/lecture-python-programming.fr/lectures/__pycache__/mathfoo.cpython-313.pyc',
'__builtins__': {'__name__': 'builtins',
'__doc__': "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.",
'__package__': '',
'__loader__': _frozen_importlib.BuiltinImporter,
'__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'),
'__build_class__': <function __build_class__>,
'__import__': <function __import__(name, globals=None, locals=None, fromlist=(), level=0)>,
'abs': <function abs(x, /)>,
'all': <function all(iterable, /)>,
'any': <function any(iterable, /)>,
'ascii': <function ascii(obj, /)>,
'bin': <function bin(number, /)>,
'breakpoint': <function breakpoint(*args, **kws)>,
'callable': <function callable(obj, /)>,
'chr': <function chr(i, /)>,
'compile': <function compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1, *, _feature_version=-1)>,
'delattr': <function delattr(obj, name, /)>,
'dir': <function dir>,
'divmod': <function divmod(x, y, /)>,
'eval': <function eval(source, /, globals=None, locals=None)>,
'exec': <function exec(source, /, globals=None, locals=None, *, closure=None)>,
'format': <function format(value, format_spec='', /)>,
'getattr': <function getattr>,
'globals': <function globals()>,
'hasattr': <function hasattr(obj, name, /)>,
'hash': <function hash(obj, /)>,
'hex': <function hex(number, /)>,
'id': <function id(obj, /)>,
'input': <bound method Kernel.raw_input of <ipykernel.ipkernel.IPythonKernel object at 0x7fc147e312b0>>,
'isinstance': <function isinstance(obj, class_or_tuple, /)>,
'issubclass': <function issubclass(cls, class_or_tuple, /)>,
'iter': <function iter>,
'aiter': <function aiter(async_iterable, /)>,
'len': <function len(obj, /)>,
'locals': <function locals()>,
'max': <function max>,
'min': <function min>,
'next': <function next>,
'anext': <function anext>,
'oct': <function oct(number, /)>,
'ord': <function ord(character, /)>,
'pow': <function pow(base, exp, mod=None)>,
'print': <function print(*args, sep=' ', end='\n', file=None, flush=False)>,
'repr': <function repr(obj, /)>,
'round': <function round(number, ndigits=None)>,
'setattr': <function setattr(obj, name, value, /)>,
'sorted': <function sorted(iterable, /, *, key=None, reverse=False)>,
'sum': <function sum(iterable, /, start=0)>,
'vars': <function vars>,
'None': None,
'Ellipsis': Ellipsis,
'NotImplemented': NotImplemented,
'False': False,
'True': True,
'bool': bool,
'memoryview': memoryview,
'bytearray': bytearray,
'bytes': bytes,
'classmethod': classmethod,
'complex': complex,
'dict': dict,
'enumerate': enumerate,
'filter': filter,
'float': float,
'frozenset': frozenset,
'property': property,
'int': int,
'list': list,
'map': map,
'object': object,
'range': range,
'reversed': reversed,
'set': set,
'slice': slice,
'staticmethod': staticmethod,
'str': str,
'super': super,
'tuple': tuple,
'type': type,
'zip': zip,
'__debug__': True,
'BaseException': BaseException,
'BaseExceptionGroup': BaseExceptionGroup,
'Exception': Exception,
'GeneratorExit': GeneratorExit,
'KeyboardInterrupt': KeyboardInterrupt,
'SystemExit': SystemExit,
'ArithmeticError': ArithmeticError,
'AssertionError': AssertionError,
'AttributeError': AttributeError,
'BufferError': BufferError,
'EOFError': EOFError,
'ImportError': ImportError,
'LookupError': LookupError,
'MemoryError': MemoryError,
'NameError': NameError,
'OSError': OSError,
'ReferenceError': ReferenceError,
'RuntimeError': RuntimeError,
'StopAsyncIteration': StopAsyncIteration,
'StopIteration': StopIteration,
'SyntaxError': SyntaxError,
'SystemError': SystemError,
'TypeError': TypeError,
'ValueError': ValueError,
'Warning': Warning,
'FloatingPointError': FloatingPointError,
'OverflowError': OverflowError,
'ZeroDivisionError': ZeroDivisionError,
'BytesWarning': BytesWarning,
'DeprecationWarning': DeprecationWarning,
'EncodingWarning': EncodingWarning,
'FutureWarning': FutureWarning,
'ImportWarning': ImportWarning,
'PendingDeprecationWarning': PendingDeprecationWarning,
'ResourceWarning': ResourceWarning,
'RuntimeWarning': RuntimeWarning,
'SyntaxWarning': SyntaxWarning,
'UnicodeWarning': UnicodeWarning,
'UserWarning': UserWarning,
'BlockingIOError': BlockingIOError,
'ChildProcessError': ChildProcessError,
'ConnectionError': ConnectionError,
'FileExistsError': FileExistsError,
'FileNotFoundError': FileNotFoundError,
'InterruptedError': InterruptedError,
'IsADirectoryError': IsADirectoryError,
'NotADirectoryError': NotADirectoryError,
'PermissionError': PermissionError,
'ProcessLookupError': ProcessLookupError,
'TimeoutError': TimeoutError,
'IndentationError': IndentationError,
'_IncompleteInputError': _IncompleteInputError,
'IndexError': IndexError,
'KeyError': KeyError,
'ModuleNotFoundError': ModuleNotFoundError,
'NotImplementedError': NotImplementedError,
'PythonFinalizationError': PythonFinalizationError,
'RecursionError': RecursionError,
'UnboundLocalError': UnboundLocalError,
'UnicodeError': UnicodeError,
'BrokenPipeError': BrokenPipeError,
'ConnectionAbortedError': ConnectionAbortedError,
'ConnectionRefusedError': ConnectionRefusedError,
'ConnectionResetError': ConnectionResetError,
'TabError': TabError,
'UnicodeDecodeError': UnicodeDecodeError,
'UnicodeEncodeError': UnicodeEncodeError,
'UnicodeTranslateError': UnicodeTranslateError,
'ExceptionGroup': ExceptionGroup,
'EnvironmentError': OSError,
'IOError': OSError,
'open': <function _io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)>,
'copyright': Copyright (c) 2001-2024 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.,
'credits': Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software
Foundation, and a cast of thousands for supporting Python
development. See www.python.org for more information.,
'license': Type license() to see the full license text,
'help': Type help() for interactive help, or help(object) for help about object.,
'execfile': <function _pydev_bundle._pydev_execfile.execfile(file, glob=None, loc=None)>,
'runfile': <function _pydev_bundle.pydev_umd.runfile(filename, args=None, wdir=None, namespace=None)>,
'__IPYTHON__': True,
'display': <function IPython.core.display_functions.display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, raw=False, clear=False, **kwargs)>,
'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7fc144b1c050>>},
'pi': 'foobar'}
Comme vous le savez, nous accédons aux éléments de l’espace de nommage en utilisant la notation d’attribut par point
math.pi
3.141592653589793
Cela est entièrement équivalent à math.__dict__['pi']
math.__dict__['pi']
3.141592653589793
7.4. Consulter les espaces de nommage#
Comme nous l’avons vu ci-dessus, l’espace de nommage math peut être affiché en tapant math.__dict__.
Une autre façon d’en voir le contenu est de taper vars(math)
vars(math).items()
dict_items([('__name__', 'math'), ('__doc__', 'This module provides access to the mathematical functions\ndefined by the C standard.'), ('__package__', ''), ('__loader__', <_frozen_importlib_external.ExtensionFileLoader object at 0x7fc14ab2da30>), ('__spec__', ModuleSpec(name='math', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x7fc14ab2da30>, origin='/home/runner/miniconda3/envs/quantecon/lib/python3.13/lib-dynload/math.cpython-313-x86_64-linux-gnu.so')), ('acos', <built-in function acos>), ('acosh', <built-in function acosh>), ('asin', <built-in function asin>), ('asinh', <built-in function asinh>), ('atan', <built-in function atan>), ('atan2', <built-in function atan2>), ('atanh', <built-in function atanh>), ('cbrt', <built-in function cbrt>), ('ceil', <built-in function ceil>), ('copysign', <built-in function copysign>), ('cos', <built-in function cos>), ('cosh', <built-in function cosh>), ('degrees', <built-in function degrees>), ('dist', <built-in function dist>), ('erf', <built-in function erf>), ('erfc', <built-in function erfc>), ('exp', <built-in function exp>), ('exp2', <built-in function exp2>), ('expm1', <built-in function expm1>), ('fabs', <built-in function fabs>), ('factorial', <built-in function factorial>), ('floor', <built-in function floor>), ('fma', <built-in function fma>), ('fmod', <built-in function fmod>), ('frexp', <built-in function frexp>), ('fsum', <built-in function fsum>), ('gamma', <built-in function gamma>), ('gcd', <built-in function gcd>), ('hypot', <built-in function hypot>), ('isclose', <built-in function isclose>), ('isfinite', <built-in function isfinite>), ('isinf', <built-in function isinf>), ('isnan', <built-in function isnan>), ('isqrt', <built-in function isqrt>), ('lcm', <built-in function lcm>), ('ldexp', <built-in function ldexp>), ('lgamma', <built-in function lgamma>), ('log', <built-in function log>), ('log1p', <built-in function log1p>), ('log10', <built-in function log10>), ('log2', <built-in function log2>), ('modf', <built-in function modf>), ('pow', <built-in function pow>), ('radians', <built-in function radians>), ('remainder', <built-in function remainder>), ('sin', <built-in function sin>), ('sinh', <built-in function sinh>), ('sqrt', <built-in function sqrt>), ('tan', <built-in function tan>), ('tanh', <built-in function tanh>), ('sumprod', <built-in function sumprod>), ('trunc', <built-in function trunc>), ('prod', <built-in function prod>), ('perm', <built-in function perm>), ('comb', <built-in function comb>), ('nextafter', <built-in function nextafter>), ('ulp', <built-in function ulp>), ('__file__', '/home/runner/miniconda3/envs/quantecon/lib/python3.13/lib-dynload/math.cpython-313-x86_64-linux-gnu.so'), ('pi', 3.141592653589793), ('e', 2.718281828459045), ('tau', 6.283185307179586), ('inf', inf), ('nan', nan)])
Si vous souhaitez seulement voir les noms, vous pouvez taper
# Affiche les 10 premiers noms
dir(math)[0:10]
['__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'acos',
'acosh',
'asin',
'asinh']
Remarquez les noms spéciaux __doc__ et __name__.
Ceux-ci sont initialisés dans l’espace de nommage chaque fois qu’un module est importé
__doc__est la chaîne de documentation du module__name__est le nom du module
print(math.__doc__)
This module provides access to the mathematical functions
defined by the C standard.
math.__name__
'math'
7.5. Sessions interactives#
En Python, tout code exécuté par l’interpréteur s’exécute dans un module.
Qu’en est-il des commandes tapées à l’invite ?
Elles sont également considérées comme étant exécutées au sein d’un module — dans ce cas, un module appelé __main__.
Pour vérifier cela, nous pouvons examiner le nom du module actuel via la valeur de __name__ donnée à l’invite
print(__name__)
__main__
Lorsque nous exécutons un script à l’aide de la commande %run d’IPython, le contenu du fichier est également exécuté comme faisant partie de __main__.
Pour le voir, créons un fichier mod.py qui affiche son propre attribut __name__
%%file mod.py
print(__name__)
Writing mod.py
Maintenant, examinons deux façons différentes de l’exécuter dans IPython
import mod # Importation standard
mod
%run mod.py # Exécution interactive
__main__
Dans le second cas, le code est exécuté comme faisant partie de __main__, donc __name__ est égal à __main__.
Pour voir le contenu de l’espace de nommage de __main__, nous utilisons vars() plutôt que vars(__main__).
Si vous faites cela dans IPython, vous verrez tout un tas de variables dont IPython a besoin et qu’il a initialisées lorsque vous avez démarré votre session.
Si vous préférez ne voir que les variables que vous avez initialisées, utilisez %whos
x = 2
y = 3
import numpy as np
%whos
Variable Type Data/Info
--------------------------------
f function <function f at 0x7fc1448b6d40>
g function <function f at 0x7fc1448b6d40>
math module <module 'math' from '/hom<...>313-x86_64-linux-gnu.so'>
mathfoo module <module 'mathfoo' from '/<...>.fr/lectures/mathfoo.py'>
mod module <module 'mod' from '/home<...>ming.fr/lectures/mod.py'>
np module <module 'numpy' from '/ho<...>kages/numpy/__init__.py'>
x int 2
y int 3
7.6. L’espace de nommage global#
La documentation Python fait souvent référence à « l’espace de nommage global ».
L’espace de nommage global est l’espace de nommage du module en cours d’exécution.
Par exemple, supposons que nous démarrions l’interpréteur et commencions à faire des affectations.
Nous travaillons maintenant dans le module __main__, et donc l’espace de nommage de __main__ est l’espace de nommage global.
Ensuite, nous importons un module appelé amodule
import amodule
À ce stade, l’interpréteur crée un espace de nommage pour le module amodule et commence à exécuter les commandes du module.
Pendant ce temps, l’espace de nommage amodule.__dict__ est l’espace de nommage global.
Une fois l’exécution du module terminée, l’interpréteur revient au module depuis lequel l’instruction d’importation a été faite.
Dans ce cas, il s’agit de __main__, donc l’espace de nommage de __main__ redevient l’espace de nommage global.
7.7. Espaces de nommage locaux#
Fait important : lorsque nous appelons une fonction, l’interpréteur crée un espace de nommage local pour cette fonction, et y enregistre les variables.
La raison de ceci sera expliquée dans un instant.
Les variables de l’espace de nommage local sont appelées variables locales.
Lorsque l’exécution de la fonction se termine, son espace de nommage local est oublié.
Pendant que la fonction s’exécute, nous pouvons voir le contenu de l’espace de nommage local avec locals().
Par exemple, considérons
def f(x):
a = 2
print(locals())
return a * x
Maintenant, appelons la fonction
f(1)
{'x': 1, 'a': 2}
2
Vous pouvez voir l’espace de nommage local de f avant qu’il ne soit détruit.
7.8. L’espace de nommage __builtins__#
Nous avons utilisé diverses fonctions natives, telles que max(), dir(), str(), list(), len(), range(), type(), etc.
Comment fonctionne l’accès à ces noms ?
Ces définitions sont fournies par le module
builtins.Elles disposent de leur propre espace de nommage appelé
__builtins__.
# Affiche les 10 premiers noms dans `__main__`
dir()[0:10]
['In', 'Out', '_', '_10', '_11', '_12', '_13', '_14', '_15', '_16']
# Affiche les 10 premiers noms dans `__builtins__`
dir(__builtins__)[0:10]
['ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'BaseExceptionGroup',
'BlockingIOError',
'BrokenPipeError',
'BufferError',
'BytesWarning',
'ChildProcessError']
Nous pouvons accéder aux éléments de l’espace de nommage comme suit
__builtins__.max
<function max>
Mais __builtins__ est spécial, car nous pouvons toujours y accéder directement aussi
max
<function max>
__builtins__.max == max
True
La section suivante explique comment cela fonctionne…
7.9. Résolution des noms#
Les espaces de nommage sont formidables car ils nous aident à organiser les noms de variables.
(Tapez import this à l’invite et regardez le dernier élément affiché)
Cependant, nous devons comprendre comment l’interpréteur Python travaille avec plusieurs espaces de nommage.
Comprendre le flux d’exécution nous aidera à déterminer quelles variables sont accessibles dans une portée donnée et comment les utiliser lors de l’écriture et du débogage des programmes.
À tout moment de l’exécution, il existe en fait au moins deux espaces de nommage accessibles directement.
(« Accessible directement » signifie sans utiliser de point, comme dans pi plutôt que math.pi)
Ces espaces de nommage sont
L’espace de nommage global (du module en cours d’exécution)
L’espace de nommage des primitives
Si l’interpréteur exécute une fonction, alors les espaces de nommage directement accessibles sont
L’espace de nommage local de la fonction
L’espace de nommage global (du module en cours d’exécution)
L’espace de nommage des primitives
Parfois, des fonctions sont définies à l’intérieur d’autres fonctions, comme ceci
def f():
a = 2
def g():
b = 4
print(a * b)
g()
Ici, f est la fonction englobante de g, et chaque fonction dispose de ses propres espaces de nommage.
Maintenant, nous pouvons énoncer la règle qui régit la résolution des espaces de nommage :
L’ordre dans lequel l’interpréteur recherche les noms est
l’espace de nommage local (s’il existe)
la hiérarchie des espaces de nommage englobants (s’ils existent)
l’espace de nommage global
l’espace de nommage des primitives
Si le nom ne se trouve dans aucun de ces espaces de nommage, l’interpréteur lève une erreur NameError.
C’est ce qu’on appelle la règle LEGB (Local, Enclosing, Global, Built-in, c’est-à-dire local, englobant, global et natif).
Voici un exemple qui aide à illustrer cela.
Les visualisations ici sont créées par nbtutor dans un notebook Jupyter.
Elles peuvent vous aider à mieux comprendre votre programme lorsque vous apprenez un nouveau langage.
Considérons un script test.py qui ressemble à ce qui suit
%%file test.py
def g(x):
a = 1
x = x + a
return x
a = 0
y = g(10)
print("a = ", a, "y = ", y)
Writing test.py
Que se passe-t-il lorsque nous exécutons ce script ?
%run test.py
a = 0 y = 11
Tout d’abord,
L’espace de nommage global
{}est créé.
L’objet fonction est créé, et
glui est lié au sein de l’espace de nommage global.Le nom
aest lié à0, encore une fois dans l’espace de nommage global.
Ensuite, g est appelé via y = g(10), ce qui conduit à la séquence d’actions suivante
L’espace de nommage local de la fonction est créé.
Les noms locaux
xetasont liés, de sorte que l’espace de nommage local devient{'x': 10, 'a': 1}.
Notez que le a global n’a pas été affecté par le a local.
L’instruction
x = x + autilise lealocal et lexlocal pour calculerx + a, et lie le nom localxau résultat.Cette valeur est renvoyée, et
ylui est lié dans l’espace de nommage global.Les
xetalocaux sont supprimés (et l’espace de nommage local est libéré).
7.9.1. Paramètres mutables et immuables#
C’est le bon moment pour en dire un peu plus sur les objets mutables et immuables.
Considérons le segment de code
def f(x):
x = x + 1
return x
x = 1
print(f(x), x)
2 1
Nous comprenons maintenant ce qui va se passer ici : le code affiche 2 comme valeur de f(x) et 1 comme valeur de x.
Tout d’abord, f et x sont enregistrés dans l’espace de nommage global.
L’appel f(x) crée un espace de nommage local et y ajoute x, lié à 1.
Ensuite, ce x local est relié au nouvel objet entier 2, et cette valeur est renvoyée.
Rien de tout cela n’affecte le x global.
Cependant, c’est une autre histoire lorsque nous utilisons un type de données mutable tel qu’une liste
def f(x):
x[0] = x[0] + 1
return x
x = [1]
print(f(x), x)
[2] [2]
Cela affiche [2] comme valeur de f(x) et également [2] comme valeur de x.
Voici ce qui se passe
fest enregistré comme fonction dans l’espace de nommage global
xest lié à[1]dans l’espace de nommage global
L’appel
f(x)Crée un espace de nommage local
Ajoute
xà l’espace de nommage local, lié à[1]
Note
Le x global et le x local se réfèrent au même [1]
Nous pouvons voir que l’identité du x local et l’identité du x global sont les mêmes
def f(x):
x[0] = x[0] + 1
print(f"l'identité de x local est {id(x)}")
return x
x = [1]
print(f"l'identité de x global est {id(x)}")
print(f(x), x)
l'identité de x global est 140468048983680
l'identité de x local est 140468048983680
[2] [2]
Au sein de
f(x)La liste
[1]est modifiée en[2]Renvoie la liste
[2]
L’espace de nommage local est libéré, et le
xlocal est perdu
Si vous souhaitez modifier le x local et le x global séparément, vous pouvez créer une copie de la liste et affecter la copie au x local.
Nous vous laissons explorer cela.