7. Noms et espaces de noms#
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.
Sous le capot, 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)
139655055732496
Dans ce cas, après avoir relié x à 'bar', aucun nom n’est lié au premier objet 'foo'.
Cela déclenche le ramassage de 'foo' par le garbage collector.
Autrement dit, l’emplacement mémoire qui stocke cet objet est libéré et rendu au système d’exploitation.
Le ramassage des ordures (garbage collection) est en réalité un domaine de recherche actif en informatique.
Vous pouvez en lire davantage sur le garbage collection si cela vous intéresse.
7.3. Espaces de noms#
Rappelons de la discussion précédente 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 noms.
Définition
Un espace de noms est une table de symboles qui associe des noms à des objets en mémoire.
Python utilise plusieurs espaces de noms, 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 noms 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 noms 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 0x7f03fba29a30>), ('__spec__', ModuleSpec(name='math', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x7f03fba29a30>, 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 0x7f03f48212b0>,
'__spec__': ModuleSpec(name='mathfoo', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f03f48212b0>, 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 0x7f03f8eb23c0>>,
'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 0x7f03f4a23cb0>>},
'pi': 'foobar'}
Comme vous le savez, nous accédons aux éléments de l’espace de noms 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. Visualiser les espaces de noms#
Comme nous l’avons vu ci-dessus, l’espace de noms 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 0x7f03fba29a30>), ('__spec__', ModuleSpec(name='math', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x7f03fba29a30>, 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 noms chaque fois qu’un module est importé
__doc__est la docstring 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 noms 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 0x7f03f4841ee0>
g function <function f at 0x7f03f4841ee0>
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 noms global#
La documentation Python fait souvent référence à « l’espace de noms global ».
L’espace de noms global est l’espace de noms 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 noms de __main__ est l’espace de noms global.
Ensuite, nous importons un module appelé amodule
import amodule
À ce stade, l’interpréteur crée un espace de noms pour le module amodule et commence à exécuter les commandes du module.
Pendant ce temps, l’espace de noms amodule.__dict__ est l’espace de noms 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 noms de __main__ redevient l’espace de noms global.
7.7. Espaces de noms locaux#
Fait important : lorsque nous appelons une fonction, l’interpréteur crée un espace de noms 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 noms local sont appelées variables locales.
Une fois la fonction terminée, l’espace de noms est libéré et perdu.
Pendant que la fonction s’exécute, nous pouvons voir le contenu de l’espace de noms 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 noms local de f avant qu’il ne soit détruit.
7.8. L’espace de noms __builtins__#
Nous avons utilisé diverses fonctions intégrées, telles que max(), dir(), str(), list(), len(), range(), type(), etc.
Comment fonctionne l’accès à ces noms ?
Ces définitions sont stockées dans un module appelé
__builtin__.Elles disposent de leur propre espace de noms 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 noms 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 noms 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 noms.
Comprendre le flux d’exécution nous aidera à vérifier quelles variables sont dans la portée et comment opérer sur elles 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 noms accessibles directement.
(« Accessible directement » signifie sans utiliser de point, comme dans pi plutôt que math.pi)
Ces espaces de noms sont
L’espace de noms global (du module en cours d’exécution)
L’espace de noms builtin
Si l’interpréteur exécute une fonction, alors les espaces de noms directement accessibles sont
L’espace de noms local de la fonction
L’espace de noms global (du module en cours d’exécution)
L’espace de noms builtin
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 noms.
Maintenant, nous pouvons énoncer la règle qui régit la résolution des espaces de noms :
L’ordre dans lequel l’interpréteur recherche les noms est
l’espace de noms local (s’il existe)
la hiérarchie des espaces de noms englobants (s’ils existent)
l’espace de noms global
l’espace de noms builtin
Si le nom ne se trouve dans aucun de ces espaces de noms, l’interpréteur lève une erreur NameError.
C’est ce qu’on appelle la règle LEGB (local, englobant, global, builtin).
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 noms global
{}est créé.
L’objet fonction est créé, et
glui est lié au sein de l’espace de noms global.Le nom
aest lié à0, encore une fois dans l’espace de noms global.
Ensuite, g est appelé via y = g(10), ce qui conduit à la séquence d’actions suivante
L’espace de noms local de la fonction est créé.
Les noms locaux
xetasont liés, de sorte que l’espace de noms 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 retournée, et
ylui est lié dans l’espace de noms global.Les
xetalocaux sont supprimés (et l’espace de noms local est libéré).
7.9.1. Paramètres Mutables versus Immuables#
C’est le bon moment pour en dire un peu plus sur les objets mutables versus 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 noms global.
L’appel f(x) crée un espace de noms local et y ajoute x, lié à 1.
Ensuite, ce x local est relié au nouvel objet entier 2, et cette valeur est retourné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 la même chose pour x.
Voici ce qui se passe
fest enregistré comme fonction dans l’espace de noms global
xest lié à[1]dans l’espace de noms global
L’appel
f(x)Crée un espace de noms local
Ajoute
xà l’espace de noms 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'the identity of local x is {id(x)}')
return x
x = [1]
print(f'the identity of global x is {id(x)}')
print(f(x), x)
the identity of global x is 139654957143552
the identity of local x is 139654957143552
[2] [2]
Au sein de
f(x)La liste
[1]est modifiée en[2]Retourne la liste
[2]
L’espace de noms 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.