Utilisation et création de variables globales dans vos fonctions Python
Une variable globale est une variable que vous pouvez utiliser à partir de n'importe quelle partie d'un programme, y compris dans les fonctions. Utiliser des variables globales dans vos fonctions Python peut être délicat. Vous devrez faire la différence entre l'accès et la modification des valeurs de la variable globale cible si vous souhaitez que votre code fonctionne correctement.
Les variables globales peuvent jouer un rôle fondamental dans de nombreux projets logiciels car elles permettent le partage de données dans l'ensemble d'un programme. Cependant, vous devez les utiliser judicieusement pour éviter les problèmes.
Dans ce didacticiel, vous allez :
- Comprendre les variables globales et leur fonctionnement en Python
- Accéder directement aux variables globales dans vos fonctions Python
- Modifier et créer des variables globales dans les fonctions à l'aide du mot-clé
global
- Accédez, créez et modifiez des variables globales dans vos fonctions avec la fonction
globals()
- Explorez des stratégies pour éviter d'utiliser des variables globales dans le code Python
Pour suivre ce didacticiel, vous devez avoir une solide compréhension de la programmation Python, y compris des concepts fondamentaux tels que les variables, les types de données, la portée, la mutabilité, les fonctions et les classes.
Utilisation de variables globales dans les fonctions Python
Les variables globales sont celles auxquelles vous pouvez accéder et modifier depuis n'importe où dans votre code. En Python, vous définirez généralement des variables globales au niveau du module. Ainsi, le module conteneur est leur portée.
Remarque : Vous pouvez également définir des variables globales dans des fonctions, comme vous l'apprendrez dans la section Création de variables globales dans une fonction.
Une fois que vous avez défini une variable globale, vous pouvez l'utiliser depuis le module lui-même ou depuis d'autres modules de votre code. Vous pouvez également utiliser des variables globales dans vos fonctions. Cependant, ces cas peuvent devenir un peu déroutants en raison des différences entre accéder et modifier les variables globales dans les fonctions.
Pour comprendre ces différences, considérons que Python peut rechercher des variables dans quatre portées différentes :
- La portée locale, ou au niveau de la fonction, qui existe à l'intérieur des fonctions
- La portée englobante, ou non locale, qui apparaît dans les fonctions imbriquées
- Le périmètre global, qui existe au niveau du module
- La portée intégrée, qui est une portée spéciale pour les noms intégrés de Python
Pour illustrer, disons que vous êtes à l’intérieur d’une fonction interne. Dans ce cas, Python peut rechercher des noms dans les quatre étendues.
Lorsque vous accédez à une variable dans cette fonction interne, Python regarde d'abord à l'intérieur de cette fonction. Si la variable n’existe pas, alors Python continue avec la portée englobante de la fonction externe. Si la variable n'y est pas définie non plus, alors Python passe aux étendues globales et intégrées dans cet ordre. Si Python trouve la variable, vous récupérez la valeur. Sinon, vous obtenez une NameError
:
>>> # Global scope
>>> def outer_func():
... # Non-local scope
... def inner_func():
... # Local scope
... print(some_variable)
... inner_func()
...
>>> outer_func()
Traceback (most recent call last):
...
NameError: name 'some_variable' is not defined
>>> some_variable = "Hello from global scope!"
>>> outer_func()
Hello from global scope!
Lorsque vous lancez une session interactive, elle démarre au niveau du module de portée globale. Dans cet exemple, vous avez outer_func()
, qui définit inner_func()
comme une fonction imbriquée. Du point de vue de cette fonction imbriquée, son propre bloc de code représente la portée locale, tandis que le bloc de code outer_func()
avant l'appel à inner_func()
représente la portée non locale. portée.
Si vous appelez outer_func()
sans définir some_variable
dans aucune de vos étendues actuelles, vous obtenez une exception NameError
car le nom n'est pas défini .
Si vous définissez some_variable
dans la portée globale et que vous appelez ensuite outer_func()
, alors vous obtenez Bonjour !
sur votre écran. En interne, Python a recherché les portées locale, non locale et globale pour trouver some_variable
et imprimer son contenu. Notez que vous pouvez définir cette variable dans l’une des trois portées et Python la trouvera.
Ce mécanisme de recherche permet d'utiliser des variables globales depuis des fonctions internes. Cependant, en profitant de cette fonctionnalité, vous pouvez rencontrer quelques problèmes. Par exemple, accéder à une variable fonctionne, mais modifier directement une variable ne fonctionne pas :
>>> number = 42
>>> def access_number():
... return number
...
>>> access_number()
42
>>> def modify_number():
... number = 7
...
>>> modify_number()
>>> number
42
La fonction access_number()
fonctionne correctement. Il recherche le numéro
et le trouve dans la portée globale. En revanche, modify_number()
ne fonctionne pas comme prévu. Pourquoi cette fonction ne met-elle pas à jour la valeur de votre variable globale, number
? Le problème est la portée de la variable. Vous ne pouvez pas modifier directement une variable depuis une portée de haut niveau comme global dans une portée de niveau inférieur comme local.
En interne, Python suppose que tout nom directement attribué dans une fonction est local à cette fonction. Par conséquent, le nom local, number
, masque son frère global.
En ce sens, les variables globales se comportent comme des noms en lecture seule. Vous pouvez accéder à leurs valeurs, mais vous ne pouvez pas les modifier.
Remarque : La discussion sur la modification des variables globales à l'intérieur des fonctions tourne autour des opérations d'affectation plutôt que des mutations sur place d'objets mutables. Vous découvrirez les effets de la mutabilité sur les variables globales dans la section Comprendre comment la mutabilité affecte les variables globales.
Obtenir une exception UnboundLocalError
est un autre problème courant lorsque vous essayez de modifier une variable globale dans une fonction. Considérez la fonction démonstrative suivante qui tente d'utiliser certaines variables globales :
>>> a = 10
>>> b = 20
>>> c = 30
>>> def print_globals():
... print(a, b, c)
... c = 100
... print(c)
...
Dans print_globals()
, vous imprimez d'abord les variables globales a
, b
et c
. Ensuite, vous mettez à jour directement la valeur de c
. Enfin, vous imprimez la version mise à jour de c
. Vous pouvez vous attendre au résultat suivant :
>>> # Expected output
>>> print_globals()
10 20 30
100
Cependant, cette sortie n'apparaît jamais sur votre écran. Au lieu de cela, vous obtenez une erreur qui pourrait vous surprendre :
>>> print_globals()
Traceback (most recent call last):
...
UnboundLocalError: cannot access local variable 'c'
where it is not associated with a value
Le problème est que l'affectation c=100
crée une nouvelle variable locale qui remplace la variable globale d'origine, c
. Donc, vous avez un conflit de nom. L'exception vient du premier appel à print()
car, à ce moment-là, la version locale de c
n'est pas encore définie. Ainsi, vous obtenez le UnboundLocalError
.
En pratique, ce problème survient le plus souvent dans les opérations d'affectation augmentée avec des variables globales :
>>> counter = 0
>>> def increment():
... counter += 1
...
>>> increment()
Traceback (most recent call last):
...
UnboundLocalError: cannot access local variable 'counter'
where it is not associated with a value
En Python, si votre fonction fait simplement référence à une variable de la portée globale, alors la fonction suppose que la variable est globale. Si vous attribuez le nom de la variable n'importe où dans le corps de la fonction, vous définissez alors une nouvelle variable locale portant le même nom que votre variable globale d'origine.
Dans une fonction, vous ne pouvez pas accéder directement à une variable globale si vous avez défini des variables locales portant le même nom n'importe où dans la fonction.
Si vous souhaitez modifier une variable globale dans une fonction, vous devez alors indiquer explicitement à Python d'utiliser la variable globale plutôt que d'en créer une nouvelle locale. Pour ce faire, vous pouvez utiliser l'un des éléments suivants :
- Le mot clé
global
- La fonction intégrée
globals()
Dans les sections suivantes, vous apprendrez à utiliser les deux outils dans votre code pour modifier les variables globales depuis vos fonctions.
Le mot clé global
Vous avez déjà appris qu'accéder aux variables globales directement depuis l'intérieur d'une fonction est tout à fait possible, à moins que vous n'ayez des variables locales portant le même nom dans la fonction conteneur. En dehors de cela, que se passe-t-il si vous devez également modifier la valeur de la variable ? Dans ce cas, vous pouvez utiliser le mot-clé global
pour déclarer que vous souhaitez faire référence à la variable globale.
La syntaxe générale pour écrire une instruction globale
est la suivante :
global variable_0, variable_1, ..., variable_n
Notez que la première variable est obligatoire, tandis que les autres variables sont facultatives. Pour illustrer comment vous pouvez utiliser cette instruction, revenez à l'exemple counter
et increment()
. Vous pouvez corriger le code en procédant comme suit :
>>> counter = 0
>>> def increment():
... global counter
... counter += 1
...
>>> increment()
>>> counter
1
>>> increment()
>>> counter
2
Dans cette nouvelle version de increment()
, vous utilisez le mot-clé global
pour déclarer que vous souhaitez que la fonction mette à jour la variable globale, counter
. Avec cette instruction, vous pouvez désormais modifier la valeur de counter
dans votre fonction, comme vous pouvez le voir ci-dessus.
Remarque : Python a un mot-clé nonlocal
qui fonctionne de la même manière que global
. Il vous permet d'utiliser des variables non locales à partir de fonctions imbriquées. Les variables non locales sont celles que vous définissez dans une fonction contenant des fonctions imbriquées. Ces variables ne seront pas locales aux fonctions internes ou imbriquées.
Il est important de noter que vous devez faire vos déclarations globales avant d'utiliser la variable cible. Sinon, vous obtiendrez une SyntaxError
:
>>> counter = 0
>>> def increment():
... counter += 1
... global counter
...
SyntaxError: name 'counter' is assigned to before global declaration
Dans cet exemple, vous essayez d'incrémenter counter
sans le déclarer comme global dans increment()
. Par conséquent, vous obtenez une SyntaxError
.
L'instruction global
indique à l'interpréteur Python lorsque vous trouvez le nom counter
dans cette fonction, il fait référence à la variable globale, donc n'en créez pas une locale.
En résumé, vous ne devez utiliser le mot-clé global
que si vous souhaitez réaffecter la variable globale au sein d'une fonction. Si vous avez simplement besoin de lire ou d'accéder à la valeur de la variable globale depuis votre fonction, vous n'avez pas besoin du mot-clé global
.
Même si l'objectif du mot-clé global
est de gérer les variables globales dans les fonctions, ce mot-clé n'est pas le seul outil que vous pouvez utiliser à cette fin. Vous pouvez également utiliser la fonction intégrée globals()
.
La fonction globals()
La fonction intégrée globals()
vous permet d'accéder à la table de noms de la portée globale, qui est un dictionnaire inscriptible contenant vos noms globaux actuels et leurs valeurs correspondantes. Vous pouvez utiliser cette fonction pour accéder ou modifier la valeur d'une variable globale à partir de vos fonctions.
Par exemple, cette fonction est pratique lorsque vous avez des variables locales portant le même nom que vos variables globales cibles et que vous devez toujours utiliser la variable globale à l'intérieur de la fonction :
>>> a = 10
>>> b = 20
>>> c = 30
>>> def print_globals():
... print(a, b, globals()["c"])
... c = 100
... print(c)
...
>>> print_globals()
10 20 30
100
>>> c
30
Dans cette nouvelle implémentation de print_globals()
, c
fait référence à la version locale de la variable, tandis que globals()["c"]
fait référence à la version globale.
Étant donné que globals()
renvoie un dictionnaire, vous pouvez accéder à ses clés comme vous accéderiez aux clés d'un dictionnaire normal. Notez que vous devez utiliser le nom de la variable comme chaîne pour accéder à la clé correspondante dans globals()
.
Le dictionnaire de noms renvoyé par globals()
est mutable, ce qui signifie que vous pouvez modifier la valeur des variables globales existantes en tirant parti de ce dictionnaire. Dans le dernier exemple, vous pouvez voir comment la variable globale, c
, contient toujours la même valeur.
Voici une autre version de increment()
. C'est un peu plus difficile à lire, mais ça marche :
>>> counter = 0
>>> def increment():
... globals()["counter"] += 1
...
>>> increment()
>>> counter
1
>>> increment()
>>> counter
2
Dans cet exemple, vous avez implémenté increment()
en utilisant globals()
au lieu du mot-clé global
. Les deux implémentations fonctionnent de la même manière, ce que vous pouvez confirmer à partir du contenu de counter
après des appels consécutifs à la fonction.
Notez que l'utilisation de globals()
pour accéder et mettre à jour les variables globales peut rendre votre code difficile à lire et à comprendre, en particulier pour les gros programmes. Il est généralement préférable d'utiliser le mot-clé global
à moins que vous n'ayez des variables locales portant le même nom que vos variables globales cibles.
Comprendre comment la mutabilité affecte les variables globales
Python a des types de données mutables et immuables. Les types mutables, tels que les listes et les dictionnaires, vous permettent de modifier ou de muter leurs valeurs sur place. En revanche, les types immuables, comme les nombres, les chaînes et les tuples, ne vous permettent pas de modifier leurs valeurs au rythme.
Pour illustrer rapidement le fonctionnement de la mutabilité et de l'immuabilité en Python, considérons l'exemple suivant, dans lequel vous modifiez une liste sur place, en modifiant la valeur de ses éléments ou éléments :
>>> numbers = [1, 2, 3]
>>> id(numbers)
4390329600
>>> numbers[0] = 10
>>> numbers[1] = 20
>>> numbers[2] = 30
>>> numbers
[10, 20, 30]
>>> id(numbers)
4390329600
Dans cet exemple, vous modifiez la valeur de numbers
. Étant donné que les listes Python sont des objets mutables, cette mutation ne change pas l'identité de votre objet liste, seulement sa valeur. Vous pouvez le confirmer en comparant la sortie de la fonction intégrée id()
avant et après les mutations.
Maintenant, que se passera-t-il si vous essayez d’effectuer une action similaire sur un type immuable comme un tuple ? Dans cet exemple de code, vous essayez :
>>> letters = ("a", "b", "c", "d")
>>> letters[0] = "aa"
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
Ici, vous utilisez un tuple pour stocker les lettres. Les tuples Python sont immuables, vous ne pouvez donc pas modifier leurs éléments comme vous l'avez fait avec votre liste de nombres. Si vous essayez de le faire, vous obtenez une TypeError
.
Lorsqu'il s'agit d'utiliser des variables globales qui pointent vers des objets mutables à l'intérieur de vos fonctions, vous remarquerez qu'il est possible de modifier leurs valeurs directement sur place.
Par exemple, supposons que vous créez une application API REST. Pour plus de commodité, vous utilisez un dictionnaire global pour partager les configurations d'API dans le code de l'application. Également pour plus de commodité, vous écrivez une courte fonction qui vous permet de mettre à jour les paramètres de configuration :
>>> config = {
... "base_url": "https://example.com/api",
... "timeout": 3,
... }
>>> def update_config(**kwargs):
... for key, value in kwargs.items():
... if key in {"api_key", "base_url", "timeout"}:
... config[key] = value
... else:
... raise KeyError(key)
...
>>> update_config(api_key="111222333")
>>> config
{
'api_key': '111222333',
'base_url': 'https://example.com/api',
'timeout': 3
}
>>> update_config(timeout=5)
>>> config
{
'api_key': '111222333',
'base_url': 'https://example.com/api',
'timeout': 5
}
Dans cet exemple, la fonction update_config()
permet de mettre à jour les paramètres de configuration de l'application. La fonction prend uniquement des arguments de mots-clés. Chaque argument doit être un paramètre de configuration valide avec sa valeur correspondante. Vous vérifiez cette condition avec l'opérateur in
dans une instruction conditionnelle. Si le paramètre n'est pas valide, alors la fonction génère une KeyError
.
Étant donné que les dictionnaires Python sont des types de données mutables, vous pouvez modifier les valeurs de config
en place depuis update_config()
sans utiliser global
ou globals()
. Vous pouvez même ajouter de nouvelles paires clé-valeur s'il s'agit de paramètres de configuration valides. Par exemple, le premier appel à update_config()
ajoute le paramètre api_key
, tandis que le deuxième appel met à jour le paramètre timeout
.
Il est important de noter que les configurations et les paramètres constituent souvent une partie importante de l’état global d’une application ou d’un système. La modification d'une configuration pendant que ce système est en cours d'exécution entraîne une mutation de son état global. Vous ne devriez effectuer ce type de changement que si vous êtes très prudent. Certaines applications et systèmes logiciels demandent un redémarrage ou un rechargement après avoir modifié certains de leurs paramètres de configuration. C’est une bonne façon de faire face à ce type de mutation étatique.
Création de variables globales dans une fonction
Comme vous l'avez déjà appris, vous pouvez utiliser le mot-clé global
ou la fonction intégrée globals()
pour accéder et modifier une variable globale dans une fonction. Ce qui est vraiment intéressant et probablement surprenant, c'est que vous pouvez également utiliser ces outils pour créer des variables globales à l'intérieur d'une fonction.
Remarque : Bien que vous puissiez créer des variables globales dans des fonctions à l'aide du mot-clé global
ou de la fonction globals()
, cela est assez rare, et ce n'est pas le cas. une bonne pratique. Cela peut rendre votre code plus difficile à comprendre et à maintenir.
Il sera préférable de définir des variables globales au niveau du module en dehors de toute fonction. De cette façon, leur portée sera claire et vous ne courrez pas le risque d'obtenir une NameError
si vous utilisez la variable avant d'appeler la fonction de définition.
Dans cet exemple, vous créez une variable globale dans une fonction à l'aide du mot-clé global
:
>>> def set_global_variable():
... global number
... number = 7
...
>>> set_global_variable()
>>> number
7
Ici, la fonction set_global_variable()
utilise le mot-clé global
pour définir number
comme variable globale. Lorsque vous appelez la fonction, numéro
est défini et sa valeur est définie sur 7
. Après l'appel, vous pouvez accéder au numéro
depuis n'importe où ailleurs dans le programme.
Vous pouvez également utiliser la fonction globals()
pour définir de nouvelles variables globales dans vos fonctions. Cet outil vous offre une flexibilité supplémentaire, vous permettant de créer dynamiquement des variables globales :
>>> def dynamic_global_variable(name, value):
... globals()[name] = value
...
>>> dynamic_global_variable("number", 42)
>>> number
42
Dans cet exemple, la fonction dynamic_global_variable()
utilise la fonction globals()
pour définir une nouvelle variable globale en utilisant un nom comme chaîne et une valeur. L'appel à votre fonction crée une nouvelle variable globale appelée number
avec une valeur de 42
.
Pour un exemple plus réaliste, disons que vous disposez d'un fichier de configuration au format JSON. Vous souhaitez traiter le fichier et charger tous les paramètres de configuration directement dans votre espace de noms global afin de pouvoir utiliser ces paramètres dans d'autres parties de votre code en tant que variables globales.
Votre fichier config.json
peut ressembler à ceci :
{
"database": {
"host": "localhost",
"port": 5432,
"name": "database.db",
"user": "username",
"password": "secret"
},
"api_key": "111222333",
"base_url": "https://example.com/api",
"timeout": 60
}
Ce fichier définit une clé "database"
, dont la valeur est un dictionnaire de clés et de valeurs. Vous utiliserez ce dictionnaire comme paramètre initial pour la base de données de votre application. Le fichier définit également une clé API, une URL de base et un délai d'attente pour la connexion à une API.
Le code suivant se charge de charger le fichier JSON et de créer pour vous l'ensemble requis de variables globales :
>>> import json
>>> def set_config_key(key, value):
... globals()[key] = value
...
>>> with open("config.json") as json_config:
... for key, value in json.load(json_config).items():
... set_config_key(key, value)
...
>>> database
{
'host': 'localhost',
'port': 5432,
'name': 'database.db',
'user': 'username',
'password': 'secret'
}
>>> api_key
'000111222333'
>>> base_url
'https://example.com/api'
>>> timeout
60
Dans cet extrait de code, vous définissez set_config_key()
, qui prend key
et value
comme arguments. Ensuite, vous utilisez globals()
pour créer une variable globale en utilisant key
comme nom de variable et value
comme valeur.
Dans l'instruction with
, vous ouvrez le fichier de configuration en lecture et affectez l'objet fichier à la variable json_config
. Ensuite, vous utilisez une boucle for
pour traiter la configuration chargée et créer de nouvelles variables globales pour chaque paire clé-valeur.
Une fois ce processus terminé, vous pouvez accéder à tous vos paramètres de configuration en tant que variables globales dans votre code.
Même si définir des variables globales dans vos fonctions à l'aide du mot-clé global
ou de la fonction globals()
est parfaitement possible en Python, ce n'est pas une bonne pratique. Vous devez être prudent lorsque vous définissez des variables globales de cette manière. Ces variables deviennent disponibles pour l'ensemble de votre programme, afin que d'autres éléments de votre code puissent les modifier. Pour cette raison, vous pouvez vous retrouver avec un code difficile à comprendre, à tester, à maintenir et à déboguer.
Peut-être que vous remarquez une petite tendance. Il existe de nombreuses façons d'utiliser et de définir des variables globales dans vos fonctions, mais cela signifie-t-il que vous devriez ? Dans la section suivante, vous explorerez cette question.
Décider quand utiliser des variables globales
Au cours de votre parcours de programmation, vous rencontrerez des situations dans lesquelles l'utilisation d'une variable globale peut être appropriée, voire nécessaire. Par exemple, vous pouvez utiliser des variables globales pour gérer :
- Configurations et paramètres : si vous disposez de paramètres qui s'appliquent à l'ensemble de votre programme, l'utilisation de variables globales peut être une solution rapide qui vous permettra d'accéder à ces paramètres depuis n'importe où dans votre code.
- Petits scripts utilitaires : si vous écrivez de petits scripts pour automatiser certaines parties de votre flux de travail ou effectuer de petites tâches, l'utilisation de variables globales peut vous aider à être opérationnel plus rapidement, vous évitant ainsi de sur-ingénierier vos solutions.
- Mise en cache des données : si vous devez mettre en cache des données pour des raisons de performances, vous pouvez profiter des variables globales pour stocker les données mises en cache.
Il est important de noter que vous devez utiliser les variables globales avec parcimonie et uniquement lorsque cela est absolument nécessaire. Si vous décidez d'utiliser des variables globales dans votre code, assurez-vous de documenter leur utilisation, en notant notamment pourquoi vous comptez sur elles.
En pratique, l’utilisation de variables globales dans votre code peut présenter certains inconvénients. Par exemple, les variables globales peuvent :
- Rendre difficile le suivi des modifications apportées à la variable, car vous pouvez modifier la variable depuis n'importe où dans votre code.
- Générez des effets secondaires indésirables, car les modifications apportées à une variable globale peuvent avoir un impact sur le comportement d'autres parties de votre code.
- Rendez votre code difficile à tester et à déboguer car le comportement et le résultat de vos fonctions dépendront de la valeur actuelle de la variable globale
- Affecte la réutilisabilité de votre code, car les fonctions qui reposent sur des variables globales sont étroitement couplées à ces variables.
- Provoquer des conflits de noms, car vous pourriez accidentellement réutiliser un nom global dans différentes parties de votre code, entraînant des résultats inattendus.
- Rompre l'encapsulation car les variables globales introduisent des dépendances cachées entre les différentes parties de votre code
- Rendez votre code difficile à refactoriser car les modifications apportées à une partie du code peuvent affecter d'autres parties
Même si vous pouvez tirer parti des variables globales pour résoudre des problèmes spécifiques, vous feriez mieux de minimiser leur utilisation dans votre code. Si vous devez quand même les utiliser, vous pouvez éviter certains de leurs inconvénients en les rendant constantes. En effet, la plupart des problèmes liés à l'utilisation de variables globales proviennent de la modification de leur valeur lors de l'exécution de votre code.
Dans les sections suivantes, vous découvrirez certaines stratégies et techniques couramment utilisées pour éviter d'utiliser des variables globales dans vos fonctions.
Éviter les variables globales dans votre code et vos fonctions
Comme vous l'avez appris dans la section précédente, s'appuyer sur des variables globales dans vos fonctions et dans votre code peut provoquer quelques effets indésirables. Ces variables peuvent rendre votre code plus difficile à comprendre, à tester et à déboguer. Ils peuvent également conduire à un code moins maintenable et réutilisable. Vous devez donc utiliser les variables globales avec soin et contrôle.
Heureusement, il existe des stratégies intéressantes que vous pouvez utiliser pour minimiser le besoin de variables globales dans votre code.
Utiliser des constantes globales
La stratégie la plus intuitive pour éviter les variables globales consiste peut-être à utiliser des constantes globales. Contrairement aux variables, les constantes ne doivent pas changer de valeur lors de l'exécution du code. Ainsi, ils promeuvent l’utilisation sûre des noms mondiaux.
Remarque : Python ne prend pas en charge les constantes strictes ni les noms non réattribuables. Pour définir une constante en Python, vous devez utiliser des lettres majuscules avec des traits de soulignement. Cependant, il ne s'agit que d'une convention de dénomination et n'empêche pas les développeurs d'attribuer de nouvelles valeurs à votre constante. Vous devez donc être prudent et ne jamais écrire de code qui modifie les valeurs des constantes.
Pour illustrer comment utiliser des constantes au lieu d'utiliser des variables globales, supposons que vous disposez de certains paramètres de configuration généraux et que vous souhaitez les rendre accessibles depuis toutes les parties de votre programme. Dans ce cas, vous pouvez utiliser des constantes globales. Vous définirez généralement des constantes en haut de votre module juste après les importations.
Voici un exemple de quelques constantes en Python :
API_KEY = "111222333"
BASE_URL = "https://example.com/api"
TIMEOUT = 3
Vous pouvez utiliser des constantes pour stocker des valeurs qui ne changeront pas pendant l’exécution de votre programme. Les constantes peuvent vous aider à empêcher toute modification accidentelle des valeurs. Ils améliorent également la lisibilité et la maintenabilité du code.
Une fois que vous avez défini vos constantes, vous pouvez y accéder dans vos fonctions à travers votre code. Vous devez simplement vous assurer de les conserver inchangés. Par exemple, vous trouverez ci-dessous une fonction qui charge une image à partir de la page API principale de la NASA :
# nasa.py
import webbrowser
import requests
API_KEY = "DEMO_KEY"
BASE_URL = "https://api.nasa.gov/planetary"
TIMEOUT = 3
def load_earth_image(date):
endpoint = f"{BASE_URL}/apod"
try:
response = requests.get(
endpoint,
params={
"api_key": API_KEY,
"date": date,
},
timeout=TIMEOUT,
)
except requests.exceptions.RequestException as e:
print(f"Error connecting to API: {e}")
return
try:
url = response.json()["url"]
except KeyError:
print(f"No image available on {date}")
return
webbrowser.open(url)
Dans cet exemple, vous importez un navigateur Web
depuis la bibliothèque standard Python. Ce module vous permet d'ouvrir rapidement des URL dans votre navigateur par défaut. Ensuite, vous importez la bibliothèque requests
pour envoyer des requêtes HTTP à l'API REST cible.
Remarque : Pour en savoir plus sur l'accès aux API REST dans votre code, consultez Python et API : une combinaison gagnante pour la lecture de données publiques.
Dans load_earth_image()
, vous accédez directement aux trois constantes globales, comme vous le feriez avec n'importe quelle variable globale en Python. Cependant, vous ne modifiez la valeur d’aucune des constantes de la fonction.
Vous pouvez désormais réutiliser ces constantes dans d’autres fonctions associées. Utiliser des constantes de cette manière vous permet d’améliorer la lisibilité et la maintenabilité de votre code. De plus, comme elles sont constantes, vous n’avez pas besoin de suivre leurs valeurs actuelles lorsque vous déboguez ou testez le code. Vous connaîtrez toujours leurs valeurs.
Pour essayer l’exemple ci-dessus, continuez et exécutez le code suivant :
>>> from nasa import load_earth_image
>>> load_earth_image("2023-04-21")
L'appel à load_earth_image()
vous obtiendra l'image dont dispose la NASA pour le 21 avril 2023. L'image s'ouvrira dans votre navigateur par défaut. Vous pouvez expérimenter avec d'autres dates et obtenir vos propres images.
Prendre des arguments et renvoyer des valeurs dans les fonctions
Écrire des fonctions qui prennent des arguments et renvoient des valeurs au lieu de s'appuyer sur des variables globales est une autre bonne stratégie pour éviter les variables globales dans votre code. Ces types de fonctions sont appelés fonctions pures et constituent le fondement de la programmation fonctionnelle, qui est un paradigme de programmation populaire de nos jours.
Les fonctions pures sont souvent totalement découplées des autres parties de votre code. Plus important encore, ils ne produisent aucun effet secondaire, comme un changement d’état des variables globales. Ces fonctionnalités les rendent plus réutilisables et plus faciles à comprendre, à déboguer, à tester et à maintenir.
A titre d'exemple, voici une nouvelle implémentation de votre ancien ami increment()
sans utiliser de variable globale à l'intérieur de la fonction. Dans ce cas, la fonction prend simplement une valeur en argument et la renvoie incrémentée de un :
>>> def increment(value=0):
... return value + 1
...
>>> counter = increment()
>>> counter
1
>>> counter = increment(counter)
>>> counter
2
>>> counter = increment(counter)
>>> counter
3
Cette nouvelle version de increment()
ne s'appuie pas sur les valeurs externes des variables globales. Ainsi, son résultat ne dépendra que de son argument d’entrée. Chaque fois que vous exécutez cette fonction avec le même argument, vous obtiendrez le même résultat, ce qui rend cette fonction plus facile à tester, à comprendre et à déboguer.
La fonction est complètement découplée de toute autre partie de votre programme afin que vous puissiez la réutiliser même dans un autre programme.
Dans cet exemple, vous modifiez votre variable counter
uniquement dans la portée globale. Vous n’effectuez pas de mutations inter-portées, vous rendez donc votre code plus sûr et plus facile à comprendre.
Utiliser des classes, des méthodes et des attributs
Parfois, vous disposez d’une ou plusieurs fonctions qui opèrent sur ou avec certaines variables globales. Dans ces cas, vous pouvez penser à écrire une classe qui transforme les fonctions en méthodes et les variables globales en attributs d'instance.
Par exemple, disons que vous codez une application pour gérer votre compte bancaire. Vous commencez par créer un ensemble de fonctions qui opèrent sur le solde du compte :
# account_global.py
balance = 0
def deposit(amount):
global balance
balance += amount
print(f"Successful deposit: +${amount:,.2f}")
def withdraw(amount):
global balance
if balance - amount > 0:
balance -= amount
print(f"Successful withdrawal: -${amount:,.2f}")
else:
print("Not enough funds for this transaction")
def get_balance():
return balance
def main():
while True:
operation = input(
"What would you like to do?\n"
" d) deposit "
" w) withdraw "
" b) balance "
" q) quit\n"
"> "
)
if operation in "dD":
amount = float(input("Enter the deposit amount: "))
deposit(amount)
elif operation in "wW":
amount = float(input("Enter the withdrawal amount: "))
withdraw(amount)
elif operation in "bB":
print(f"Current balance: ${get_balance():,.2f}")
elif operation in "qQ":
print("Goodbye!")
break
else:
print("Invalid operation. Please try again.")
main()
Dans ce code, vous vous appuyez sur une variable globale pour stocker le solde de votre compte bancaire. Vous disposez de trois fonctions qui assurent des opérations courantes sur le solde du compte. Vous pouvez déposer de l'argent, retirer de l'argent et vérifier le solde actuel de votre compte.
La fonction main()
définit une boucle while
qui vous présente une interface utilisateur textuelle (TUI). Grâce à cette interface, vous pouvez demander à l'application d'effectuer les opérations souhaitées sur votre compte.
Voici comment cette application fonctionne en pratique :
$ python account_global.py
What would you like to do?
d) deposit w) withdraw b) balance q) quit
> d
Enter the deposit amount: 6000
Successful deposit: +$6,000.00
What would you like to do?
d) deposit w) withdraw b) balance q) quit
> w
Enter the withdrawal amount: 2380
Successful withdrawal: -$2,380.00
What would you like to do?
d) deposit w) withdraw b) balance q) quit
> b
Current balance: $3,620.00
What would you like to do?
d) deposit w) withdraw b) balance q) quit
> q
Goodbye!
Super! L'application fonctionne comme prévu. Il vous permet d'effectuer des dépôts, de retirer des fonds et de vérifier votre solde actuel. Dites maintenant que vous n'aimez pas trop dépendre des variables globales et que vous décidez de refactoriser l'application à l'aide d'une classe. Pour ce faire, vous devez exécuter les étapes suivantes :
- Définissez une classe avec un nom approprié.
Compte
fonctionne pour cet exemple. - Déplacez les variables globales dans la classe, en les convertissant en attributs d'instance.
- Déplacez les fonctions dans la classe en tant que méthodes d'instance.
Voici le code refactorisé :
# account_class.py
class Account:
def __init__(self, balance=0):
self.balance = balance
def deposit(self, amount):
self.balance += amount
print(f"Successful deposit: +${amount:,.2f}")
def withdraw(self, amount):
if self.balance - amount > 0:
self.balance -= amount
print(f"Successful withdrawal: -${amount:,.2f}")
else:
print("Not enough funds for this transaction")
def main():
account = Account()
while True:
operation = input(
"What would you like to do?\n"
" d) deposit "
" w) withdraw "
" b) balance "
" q) quit\n"
"> "
)
if operation in "dD":
amount = float(input("Enter the deposit amount: "))
account.deposit(amount)
elif operation in "wW":
amount = float(input("Enter the withdrawal amount: "))
account.withdraw(amount)
elif operation in "bB":
print(f"Current balance: ${account.balance:,.2f}")
elif operation in "qQ":
print("Goodbye!")
break
else:
print("Invalid operation. Please try again.")
main()
Dans cette nouvelle version de votre programme de comptabilité, vous avez défini une classe appelée Compte
. Cette classe possède un attribut d'instance pour stocker le solde du compte. Cet attribut était votre ancienne variable globale balance
.
Vous avez également converti les fonctions deposit()
et withdraw()
en méthodes d'instance avec l'instance actuelle, self
, comme premier argument. Enfin, à l'intérieur de la boucle, vous instanciez la classe Account
et appelez ses méthodes sur cette instance pour que l'application fonctionne de la même manière que l'ancienne version.
Notez que dans cette implémentation, vous n'avez pas de méthode .get_balance()
. Ainsi, pour accéder au solde du compte, vous pouvez directement utiliser l'attribut .balance
.
Conclusion
Vous avez beaucoup appris sur l'utilisation des variables globales, en particulier dans vos fonctions Python. Vous avez appris que vous pouvez accéder aux variables globales directement dans vos fonctions. Cependant, pour modifier une variable globale dans une fonction, vous devez utiliser soit le mot-clé global
, soit la fonction globals()
.
Les variables globales vous permettent de partager des données entre plusieurs fonctions, ce qui peut être utile dans certaines situations. Cependant, vous devez utiliser ce type de variable avec précaution et parcimonie pour éviter d’écrire du code difficile à comprendre, déboguer, tester et maintenir.
Dans ce didacticiel, vous avez appris à :
- Travailler avec des variables globales en Python
- Accédez directement aux variables globales dans vos fonctions Python
- Modifier et créer des variables globales dans les fonctions à l'aide du mot-clé
global
- Accédez, créez et modifiez des variables globales dans vos fonctions avec la fonction
globals()
- Utiliser des stratégies pour éviter d'utiliser des variables globales dans le code Python
Vous savez maintenant ce qu'il faut faire pour utiliser correctement les variables globales dans les fonctions en Python, afin de pouvoir les utiliser efficacement le cas échéant. Vous savez également qu’éviter les variables globales est la meilleure façon de procéder la plupart du temps. Cela vous permettra d'écrire du code plus modulaire, maintenable et réutilisable.