Comment déclarer une constante en Python ?
En Java, nous faisons :
public static final String CONST_NAME = "Nom";
Comment déclarer une constante en Python ?
En Java, nous faisons :
public static final String CONST_NAME = "Nom";
Vous ne pouvez pas déclarer une variable ou une valeur comme constante en Python.
Pour indiquer aux programmeurs qu'une variable est une constante, on l'écrit généralement en majuscules :
CONST_NAME = "Nom"
Pour générer des exceptions lorsque les constantes sont modifiées, voir Constants in Python par Alex Martelli. Notez que cela n'est pas couramment utilisé en pratique.
À partir de Python 3.8, il existe une typing.Final
annotation de variable qui indiquera aux vérificateurs de type statique (comme mypy) que votre variable ne devrait pas être réaffectée. C'est l'équivalent le plus proche du final
de Java. Cependant, cela ne empêche pas réellement la réaffectation :
from typing import Final
a: Final[int] = 1
# S'exécute correctement, mais mypy signalera une erreur si vous exécutez mypy sur ceci :
a = 2
Dans emacs mypy
ne donne aucune notation pour les réassignations :Final
. Devrais-je faire une configuration spécifique pour cela ?
Je me demande pourquoi le concept de constantes existe même. Tout change. Les variables aussi devraient le faire.
Il n'y a pas de mot-clé const
comme dans d'autres langages, cependant il est possible de créer une propriété qui possède une "fonction getter" pour lire les données, mais pas de "fonction setter" pour réécrire les données. Cela protège essentiellement l'identifiant contre tout changement.
Voici une implémentation alternative en utilisant une propriété de classe :
Notez que le code est loin d'être facile pour un lecteur se posant des questions sur les constantes. Voir l'explication ci-dessous.
def constant(f):
def fset(self, value):
raise TypeError
def fget(self):
return f()
return property(fget, fset)
class _Const(object):
@constant
def FOO():
return 0xBAADFACE
@constant
def BAR():
return 0xDEADBEEF
CONST = _Const()
print(hex(CONST.FOO)) # -> '0xbaadfaceL'
CONST.FOO = 0
##Traceback (most recent call last):
## File "example1.py", line 22, in
## CONST.FOO = 0
## File "example1.py", line 5, in fset
## raise TypeError
##TypeError
Explication du Code :
constant
qui prend une expression, et l'utilise pour construire un "getter" - une fonction qui renvoie uniquement la valeur de l'expression.constant
que nous venons de créer comme décoration pour définir rapidement des propriétés en lecture seule.Et d'une manière un peu plus ancienne :
(Le code est assez complexe, plus d'explications ci-dessous)
class _Const(object):
def FOO():
def fset(self, value):
raise TypeError
def fget(self):
return 0xBAADFACE
return property(**locals())
FOO = FOO() # Définir la propriété.
CONST = _Const()
print(hex(CONST.FOO)) # -> '0xbaadfaceL'
CONST.FOO = 0
##Traceback (most recent call last):
## File "example2.py", line 16, in
## CONST.FOO = 0
## File "example2.py", line 6, in fset
## raise TypeError
##TypeError
property
pour construire un objet qui peut être "set" ou "get".property
sont nommés fset
et fget
.property
Que dois-je faire si j'ai besoin d'un uuid constant uuuid.uuid4().hex
? La propriété empêchera la fonction uuid de changer, mais la valeur changera.
Si vous souhaitez une collection de constructions en lecture seule sous un nom commun, n'est-ce pas une implémentation plus simple d'un namedtuple
?
En Python au lieu d'imposer une langue quelque chose, les gens utilisent des conventions de nommage par exemple __method
pour les méthodes privées et en utilisant _method
pour les méthodes protégées.
Ainsi de la même manière, vous pouvez simplement déclarer la constante en majuscules, par exemple:
MY_CONSTANT = "one"
Si vous voulez que cette constante ne change jamais, vous pouvez accéder aux attributs et faire des astuces, mais une approche plus simple est de déclarer une fonction:
def MY_CONSTANT():
return "one"
Le seul problème est partout où vous devrez faire MY_CONSTANT()
, mais encore une fois MY_CONSTANT = "one"
est la bonne manière en Python (habituellement).
Vous pouvez également utiliser namedtuple() pour créer des constantes:
>>> from collections import namedtuple
>>> Constantes = namedtuple('Constantes', ['pi', 'e'])
>>> constantes = Constantes(3.14, 2,718)
>>> constantes.pi
3,14
>>> constantes.pi = 3
Traceback (most recent call last):
File "", line 1, in
AttributeError: can't set attribute
En utilisant def MY_CONSTANT(): return "one"
ne prévient pas la réaffectation de la référence de la méthode, n'est-ce pas ? N'est-ce pas exactement ainsi que fonctionne le duck typing ?
"Si vous voulez que cette constante ne change jamais" -- c'est une propriété par défaut d'une constante
J'ai récemment trouvé une mise à jour très succincte pour cela qui soulève automatiquement des messages d'erreur significatifs et empêche l'accès via __dict__
:
class CONST(object):
__slots__ = ()
FOO = 1234
CONST = CONST()
# ----------
print(CONST.FOO) # 1234
CONST.FOO = 4321 # AttributeError: l'attribut d'objet 'CONST' de 'CONST' est en lecture seule
CONST.__dict__['FOO'] = 4321 # AttributeError: l'objet 'CONST' n'a pas d'attribut '__dict__'
CONST.BAR = 5678 # AttributeError: l'objet 'CONST' n'a pas d'attribut 'BAR'
Nous nous définissons sur nous-mêmes comme pour nous rendre une instance et utilisons ensuite les slots pour garantir qu'aucun attribut supplémentaire ne puisse être ajouté. Cela supprime également le chemin d'accès via __dict__
. Bien sûr, l'ensemble de l'objet peut toujours être redéfini.
Modifier - Solution originale
Je dois probablement manquer quelque chose ici, mais cela semble fonctionner pour moi:
class CONST(object):
FOO = 1234
def __setattr__(self, *_):
pass
CONST = CONST()
#----------
print CONST.FOO # 1234
CONST.FOO = 4321
CONST.BAR = 5678
print CONST.FOO # Toujours 1234!
print CONST.BAR # Oops AttributeError
La création de l'instance permet au méthode magique __setattr__
de s'activer et intercepter les tentatives de définir la variable FOO
. Vous pourriez déclencher une exception ici si vous le souhaitez. L'instanciation de l'instance sur le nom de la classe empêche l'accès direct via la classe.
C'est une vraie douleur pour une seule valeur, mais vous pourriez attacher beaucoup de choses à votre objet CONST
. Avoir une classe supérieure avec le nom de la classe semble aussi un peu grossier, mais je pense que c'est assez succinct dans l'ensemble.
En plus des deux meilleures réponses (utiliser seulement des variables avec des noms en MAJUSCULES, ou utiliser des propriétés pour rendre les valeurs en lecture seule), je tiens à mentionner qu'il est possible d'utiliser des métaclasses pour implémenter des constantes nommées. Je propose une solution très simple en utilisant des métaclasses sur GitHub qui peut vous être utile si vous souhaitez que les valeurs soient plus informatives sur leur type/nom :
>>> from named_constants import Constants
>>> class Colors(Constants):
... black = 0
... red = 1
... white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True
Ceci est légèrement plus avancé en Python, mais reste très facile à utiliser et pratique. (Le module a quelques fonctionnalités supplémentaires, notamment des constantes en lecture seule, consultez son README.)
Il existe des solutions similaires disponibles dans divers dépôts, mais à ma connaissance, elles manquent soit d'une fonctionnalité fondamentale que l'on attend des constantes (comme être constantes, ou être de type arbitraire), soit elles ont des fonctionnalités ésotériques ajoutées qui les rendent moins généralement applicables. Mais cela peut varier, je serais reconnaissant pour vos retours. :-)
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
11 votes
En réalité, il est possible de rendre les variables en lecture seule en utilisant la fonction/décorateur property de Python. La réponse de inv est un exemple d'utilisation personnalisée de cela. Cependant, property est plus généraliste, une bonne analyse de son fonctionnement se trouve dans l'article de Shalabh Chaturvedi sur Python Attributes and Methods.
35 votes
À mon avis, imposer la constance n'est pas une pratique "pythonique". Dans Python 2.7, vous pouvez même écrire
True=False
, et ensuite(2+2==4)==True
retourneFalse
.8 votes
Comme d'autres réponses le suggèrent, il n'y a pas de moyen ou pas besoin de déclarer des constantes. Mais vous pouvez lire ce PEP sur les conventions. par exemple, THIS_IS_A_CONSTANT
52 votes
@osa : Vous ne pouvez pas le faire en python 3 -
SyntaxError: can't assign to keyword
. Cela semble être une bonne chose.10 votes
Je suis surpris que cela n'ait pas été mentionné jusqu'à présent, mais Enums semblerait être une bonne façon de définir des constantes énumérées.
3 votes
Maintenant, vous pouvez utiliser "final" depuis python.org/dev/peps/pep-0591 ou des littéraux depuis python.org/dev/peps/pep-0586
0 votes
"final" est un décorateur de typage, il ne fait rien pour rendre une valeur immuable à l'exécution. Si vous voulez une valeur immutable, l'équivalent en Python d'une constante est un enum