1386 votes

Comment créer une constante en Python?

Comment déclarer une constante en Python ?

En Java, nous faisons :

public static final String CONST_NAME = "Nom";

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 retourne False.

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

1362voto

Felix Kling Points 247451

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

14 votes

"Just don't change it." Vraiment ? Juste de l'ironie, ce n'est pas ?

0 votes

Dans emacs mypy ne donne aucune notation pour les réassignations :Final. Devrais-je faire une configuration spécifique pour cela ?

1 votes

Je me demande pourquoi le concept de constantes existe même. Tout change. Les variables aussi devraient le faire.

439voto

inv Points 1202

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 :

  1. Définir une fonction constant qui prend une expression, et l'utilise pour construire un "getter" - une fonction qui renvoie uniquement la valeur de l'expression.
  2. La fonction setter lève une TypeError donc c'est en lecture seule.
  3. Utilisez la fonction 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
  1. Pour définir l'identifiant FOO, commencez par définir deux fonctions (fset, fget - les noms sont de mon choix).
  2. Ensuite, utilisez la fonction intégrée property pour construire un objet qui peut être "set" ou "get".
  3. Note que les deux premiers paramètres de la fonction property sont nommés fset et fget.
  4. Utilisez le fait que nous avons choisi ces noms pour notre propre getter & setter et créez un dictionnaire de mots-clés en utilisant ** (double astérisque) appliqué à toutes les définitions locales de cette portée pour passer des paramètres à la fonction property

0 votes

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.

0 votes

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 ?

0 votes

C'est génial et parfaitement intégrable grâce à la classe, on peut créer différents ensembles de constantes, comme ERREURS = _Erreurs(), etc.

161voto

Anurag Uniyal Points 31931

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

7 votes

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 ?

0 votes

"Si vous voulez que cette constante ne change jamais" -- c'est une propriété par défaut d'une constante

2 votes

constants.pi = 3 échoue, mais constants = Constants(3, 2) ne le fait pas.

107voto

Jon Betts Points 256

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.

21voto

hans_meine Points 401

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.com

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.

Powered by:

X