web-dev-qa-db-fra.com

Comment créer une constante en Python?

Est-il possible de déclarer une constante en Python? En Java, nous pouvons créer des valeurs constantes de cette manière:

public static final String CONST_NAME = "Name";

Quel est l'équivalent de la déclaration de constante Java ci-dessus en Python?

869
zfranciscus

Non, il n'y en a pas. Vous ne pouvez pas déclarer une variable ou une valeur comme constante en Python. Ne le change pas.

Si vous êtes dans une classe, l'équivalent serait:

class Foo(object):
    CONST_NAME = "Name"

sinon, c'est juste

CONST_NAME = "Name"

Mais vous voudrez peut-être jeter un coup d'œil à l'extrait de code Constantes en Python par Alex Martelli.

860
Felix Kling

Il n'y a pas de mot clé const comme dans d'autres langues, mais il est possible de créer une propriété qui a une "fonction getter" pour lire les données, mais pas de "fonction setter" pour réécrire les données. Ceci protège essentiellement l'identifiant d'être modifié.

Voici une implémentation alternative utilisant la propriété de classe:

Notez que le code est loin d'être facile pour un lecteur qui s'interroge 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 CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

Explication du code:

  1. Définissez 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 afin qu'elle soit en lecture seule
  3. Utilisez la fonction constant que nous venons de créer en tant que décoration pour définir rapidement les propriétés en lecture seule.

Et d'une autre manière plus ancienne:

(le code est assez compliqué, plus d'explications ci-dessous)

class _Const(object):
    @apply
    def FOO():
        def fset(self, value):
            raise TypeError
        def fget(self):
            return 0xBAADFACE
        return property(**locals())

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

Notez que le décorateur @apply semble être obsolète.

  1. Pour définir l'identifiant FOO, les sapins définissent deux fonctions (fset, fget - les noms sont à mon choix).
  2. Utilisez ensuite la fonction intégrée property pour construire un objet pouvant être "défini" ou "obtenu".
  3. Remarquez 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 mêmes noms pour notre propre getter & setter et créez un dictionnaire de mots-clés en utilisant le ** (double astérisque) appliqué à toutes les définitions locales de cette étendue pour transmettre des paramètres à la fonction property
328
inv

Dans Python au lieu d'appliquer la langue, les utilisateurs utilisent des conventions de dénomination telles que __method pour les méthodes privées et l'utilisation de _method pour les méthodes protégées.

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 vous connecter à l'accès aux attributs et faire des astuces, mais une approche plus simple consiste à déclarer une fonction.

def MY_CONSTANT():
    return "one"

Le seul problème est que partout où vous devrez faire MY_CONSTANT (), mais encore une fois MY_CONSTANT = "one" est la manière correcte en python (généralement).

Vous pouvez également utiliser namedtuple pour créer des constantes:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
100
Anurag Uniyal

J'ai récemment trouvé une mise à jour très succincte de ce problème qui génère 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: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

Nous définissons par nous-mêmes comment faire de nous-mêmes une instance, puis nous utilisons des slots pour nous assurer qu'aucun attribut supplémentaire ne peut être ajouté. Cela supprime également la route d'accès __dict__. Bien sûr, tout l'objet peut encore être redéfini.

Edit - Solution originale

Il me manque probablement un truc 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    # Still 1234!
print CONST.BAR    # Oops AttributeError

La création de l'instance permet à la méthode magique __setattr__ d'entrer et d'intercepter les tentatives de définition de la variable FOO. Vous pouvez lancer une exception ici si vous le souhaitez. L'instanciation de l'instance sur le nom de la classe empêche l'accès directement via la classe.

C'est une douleur totale pour une valeur, mais vous pouvez attacher beaucoup à votre objet CONST. Avoir une classe supérieure, le nom de la classe semble également un peu moche, mais je pense que c'est assez succinct.

51
Jon Betts

Python n'a pas de constantes.

L’alternative la plus simple est peut-être de définir une fonction pour cela:

def MY_CONSTANT():
    return 42

MY_CONSTANT() a maintenant toutes les fonctionnalités d'une constante (plus quelques accolades gênantes).

27
Saeed Baig

En plus des deux réponses principales (il suffit d'utiliser des variables avec des noms UPPERCASE ou d'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 nommé constantes. Je propose une solution très simple en utilisant des métaclasses à GitHub , ce qui peut ê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

C'est un peu plus avancé Python, mais toujours très facile à utiliser et pratique. (Le module a quelques fonctionnalités supplémentaires, y compris des constantes en lecture seule, voir son fichier README.)

Il existe des solutions similaires dans divers référentiels, mais à ma connaissance, il leur manque soit l’une des caractéristiques fondamentales que je pourrais attendre des constantes (comme être constant ou de type arbitraire), soit des fonctionnalités ésotériques qui ajoutent les rendre moins généralement applicables. Mais YMMV, je vous serais reconnaissant de vos commentaires. :-)

18
hans_meine

Edit: Exemple de code ajouté pour Python 3

Remarque: cette autre réponse semble offrir une implémentation beaucoup plus complète similaire à la suivante (avec plus de fonctionnalités).

Commençons par faire un métaclasse :

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError

Cela empêche les propriétés statiques d'être modifiées. Créez ensuite une autre classe qui utilise cette métaclasse:

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

Ou, si vous utilisez Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

Cela devrait empêcher les accessoires d'instance d'être modifiés. Pour l'utiliser, héritez:

class MyConst(Const):
    A = 1
    B = 2

Maintenant, les accessoires, accédés directement ou via une instance, devraient être constants:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

Voici un exemple de ce qui précède en action. Voici un autre exemple pour Python 3.

16
doubleswirve

Les propriétés sont un moyen de créer des constantes. Vous pouvez le faire en déclarant une propriété getter, mais en ignorant le setter. Par exemple:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

Vous pouvez consulter n article que j'ai écrit pour trouver d'autres moyens d'utiliser les propriétés Python.

13
nvd

Voici une implémentation d'une classe "Constants", qui crée des instances avec des attributs en lecture seule (constants). Par exemple. pouvez utiliser Nums.PI pour obtenir une valeur initialisée sous la forme 3.14159, et Nums.PI = 22 lève une exception.

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__== "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

Merci à @ FrozenDict de MikeGraham , que j’ai utilisé comme point de départ. Modifié, donc au lieu de Nums['ONE'], la syntaxe d'utilisation est Nums.ONE.

Et grâce à la réponse de @ Raufio, l’idée de remplacer __ setattr __.

Ou pour une implémentation avec plus de fonctionnalités, voir @Hans_meine ' named_constants chez GitHub

8
ToolmakerSteve

Malheureusement, le Python n'a pas encore de constantes et c'est dommage. ES6 a déjà ajouté des constantes de prise en charge à JavaScript ( https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/const ) car il s'agit d'une chose très utile dans tout langage de programmation. . Comme indiqué dans d'autres réponses de la communauté Python, la variable majuscule convention-utilisateur est utilisée comme constantes, mais elle ne protège pas contre les erreurs de code arbitraires. Si vous le souhaitez, vous pourrez peut-être trouver utile une solution à fichier unique (voir docstrings comment l’utiliser).

fichier constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> Tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = Tuple(value)
            Elif isinstance(value, set):
                value = frozenset(value)
            Elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __== '__main__':
    import doctest
    doctest.testmod()

Si cela ne suffit pas, voir test complet pour cela.

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_Tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, Tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_Tuple_constant(self):

        self.const.Tuple = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.Tuple, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.Tuple = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})

Avantages: 1. Accès à toutes les constantes pour l'ensemble du projet 2. Contrôle strict des valeurs des constantes

Manques: 1. Pas de support pour les types personnalisés et le type 'dict'

Remarques:

  1. Testé avec Python3.4 et Python3.5 (je suis utiliser le "tox" pour cela)

  2. Environnement de test:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
6
Seti Volkylany

Je voudrais faire une classe qui remplace la méthode __setattr__ de la classe d'objet de base et envelopper mes constantes avec cela, notez que j'utilise python 2.7:

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)

Pour envelopper une chaîne:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

C'est assez simple, mais si vous voulez utiliser vos constantes de la même manière qu'un objet non constant (sans utiliser constObj.value), ce sera un peu plus intensif. Il est possible que cela cause des problèmes, il est donc préférable de garder le .value pour montrer et savoir que vous effectuez des opérations avec des constantes (peut-être pas le moyen le plus "pythonique").

6
Raufio

Vous pouvez utiliser une syntaxe nommée comme solution de contournement pour créer efficacement une constante qui fonctionne de la même manière qu'une variable statique finale dans Java (une Java "constante"). Les solutions de contournement sont plutôt élégantes. (Une approche plus élégante consisterait simplement à améliorer le langage Python --- quel type de langage vous permet de redéfinir _math.pi_? - mais je m'éloigne du sujet.)

(Alors que j'écris ceci, je réalise une autre réponse à cette question mentionnée namedtuple, mais je vais continuer ici car je vais montrer une syntaxe plus proche de ce que vous attendez de Java, car il n'est pas nécessaire de créer un nom nommé. tapez comme vous le nommez-le vous oblige à le faire.)

En suivant votre exemple, vous vous souviendrez que dans Java nous devons définir la constante dans une classe ; parce que vous n'avez pas mentionné un nom de classe, appelons-le Foo. Voici la classe Java:

_public class Foo {
  public static final String CONST_NAME = "Name";
}
_

Voici l'équivalent Python.

_from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')
_

Le point clé que je veux ajouter ici est que vous n’avez pas besoin d’un type séparé Foo (un "Tuple anonyme" serait Nice, même si cela ressemble à un oxymoron), nous appelons donc notre nom nommé __Foo_ afin d’espérer que cela n’échappera pas à l’importation de modules.

Le deuxième point ici est que nous créons immédiatement une instance du nametuple, en l'appelant Foo; il n'est pas nécessaire de faire cela dans une étape séparée (sauf si vous le souhaitez). Maintenant, vous pouvez faire ce que vous pouvez faire en Java:

_>>> Foo.CONST_NAME
'Name'
_

Mais vous ne pouvez pas lui attribuer:

_>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute
_

Remerciements: je pensais avoir inventé la méthode nomméetuple, mais je me suis aperçu que quelqu'un d'autre avait donné une réponse similaire (bien que moins compacte). Puis j'ai aussi remarqué Que sont les "tuples nommés" en Python? , ce qui indique que sys.version_info est maintenant un type nommé, donc peut-être que le Python La bibliothèque standard a déjà eu cette idée beaucoup plus tôt.

Notez que malheureusement (il s’agit toujours de Python), vous pouvez effacer l’attribution entière de Foo:

_>>> Foo = 'bar'
_

(facepalm)

Mais au moins, nous empêchons que la valeur _Foo.CONST_NAME_ ne soit modifiée, et c'est mieux que rien. Bonne chance.

6
Garret Wilson

Techniquement, un tuple est considéré comme une constante, car un tuple générera une erreur si vous essayez de modifier l'une de ses valeurs. Si vous voulez déclarer un tuple avec une valeur, placez une virgule après sa seule valeur, comme ceci:

my_Tuple = (0 """Or any other value""",)

Pour vérifier la valeur de cette variable, utilisez quelque chose de similaire à ceci:

if my_Tuple[0] == 0:
    #Code goes here

Si vous essayez de changer cette valeur, une erreur sera générée.

5
tobahhh

La manière pythonique de déclarer des "constantes" est essentiellement une variable de niveau module:

RED = 1
GREEN = 2
BLUE = 3

Et écrivez ensuite vos cours ou fonctions. Comme les constantes sont presque toujours des entiers et qu'elles sont également immuables en Python, vous avez très peu de chance de le modifier.

Sauf si, bien sûr, vous définissez explicitement RED = 2.

4
Xavier Ho

Il y a une manière plus propre de faire ceci avec namedtuple:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

Exemple d'utilisation

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

Avec cette approche, vous pouvez nommer vos constantes dans un espace de noms.

3

Nous pouvons créer un objet descripteur.

class Constant:
  def __init__(self,value=None):
    self.value = value
  def __get__(self,instance,owner):
    return self.value
  def __set__(self,instance,value):
    raise ValueError("You can't change a constant")

1) Si nous voulions travailler avec des constantes au niveau instance, alors:

class A:
  NULL = Constant()
  NUM = Constant(0xFF)

class B:
  NAME = Constant('bar')
  LISTA = Constant([0,1,'INFINITY'])

>>> obj=A()
>>> print(obj.NUM)  #=> 255
>>> obj.NUM =100

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: You can't change a constant

2) si nous voulions créer des constantes uniquement au niveau de la classe, nous pourrions utiliser une métaclasse servant de conteneur pour nos constantes (nos objets descripteurs); toutes les classes qui descendent hériteront de nos constantes (nos objets descripteurs) sans aucun risque pouvant être modifié.

# metaclass of my class Foo
class FooMeta(type): pass

# class Foo
class Foo(metaclass=FooMeta): pass

# I create constants in my metaclass
FooMeta.NUM = Constant(0xff)
FooMeta.NAME = Constant('FOO')

>>> Foo.NUM   #=> 255
>>> Foo.NAME  #=> 'FOO'
>>> Foo.NUM = 0 #=> ValueError: You can't change a constant

Si je crée une sous-classe de Foo, cette classe héritera de la constante sans possibilité de les modifier.

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant
3
MVP

Les dictionnaires Python sont modifiables, ils ne semblent donc pas être un bon moyen de déclarer des constantes:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
3
n8boyd

En python, une constante est simplement une variable avec un nom en majuscules, avec des mots séparés par le caractère de soulignement,

par exemple

DAYS_IN_WEEK = 7

La valeur est modifiable, car vous pouvez la changer. Mais étant donné que les règles pour le nom vous disent est une constante, pourquoi le feriez-vous? Je veux dire, c'est votre programme après tout!

C'est l'approche adoptée tout au long de python. Il n'y a pas de mot clé private pour la même raison. Préfixez le nom avec un trait de soulignement et vous savez qu'il est destiné à être privé. Le code peut enfreindre la règle .... tout comme un programmeur pourrait quand même supprimer le mot-clé privé.

Python aurait pu ajouter un mot clé const ... mais un programmeur pourrait supprimer le mot clé, puis modifier la constante s'il le souhaite, mais pourquoi le faire? Si vous voulez enfreindre la règle, vous pouvez quand même changer la règle. Mais pourquoi se donner la peine d'enfreindre la règle si le nom clarifie l'intention?

Peut-être y a-t-il un test unitaire où il est logique d'appliquer un changement à la valeur? Pour voir ce qui se passe pendant une semaine de 8 jours même si, dans le monde réel, le nombre de jours de la semaine ne peut pas être changé. Si la langue vous empêche de faire une exception s'il ne s'agit que de ce cas, vous devez enfreindre la règle ... vous devez alors cesser de la déclarer comme constante, même si c'est toujours une constante dans l'application et qu'il y a juste ce cas de test qui voit ce qui se passe s’il est modifié.

Le nom en majuscule vous indique qu'il est censé être une constante. C'est ce qui est important. Ce n’est pas un langage qui impose des contraintes au code, vous avez le pouvoir de le changer.

C'est la philosophie du python.

3
innov8

Peut-être que pconst library vous aidera ( github ).

$ pip install pconst

from pconst import const
const.Apple_PRICE = 100
const.Apple_PRICE = 200

[Out] Constant value of "Apple_PRICE" is not editable.

3
sim

Voici une astuce si vous voulez des constantes sans vous soucier de leurs valeurs:

Il suffit de définir des classes vides.

par exemple:

class RED: 
    pass
class BLUE: 
    pass
2
Lym Zoy

Vous pouvez utiliser StringVar ou IntVar, etc., votre constante est const_val

val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)

def reverse(*args):
    const_val.set(val)
2
Nqobizwe

Simplement vous pouvez simplement:

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

espère que cela rend tout beaucoup plus simple

2
user9748068

Il n'y a pas de moyen parfait pour faire ça. Si je comprends bien, la plupart des programmeurs ne font que mettre l’identifiant en majuscule, ainsi PI = 3.142 peut facilement être compris comme une constante.

D'autre part, si vous voulez quelque chose qui agit réellement comme une constante, je ne suis pas sûr que vous le trouviez. Avec tout ce que vous faites, il y aura toujours un moyen d’éditer la "constante", donc ce ne sera pas vraiment une constante. Voici un exemple très simple et sale:

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))

Cela ressemble à une constante de style PHP.

En réalité, tout ce qu'il faut à quelqu'un pour changer la valeur est le suivant:

globals()["PI"+str(id("PI"))] = 3.1415

C'est la même chose pour toutes les autres solutions que vous trouverez ici - même les plus astucieuses qui font une classe et redéfinissent la méthode de l'attribut set - il y aura toujours un moyen de les contourner. C'est comme ça que Python est.

Ma recommandation est d'éviter simplement les tracas et de capitaliser vos identifiants. Ce ne serait pas vraiment une constante appropriée mais là encore, rien ne le ferait.

2
John

En Python, les constantes n'existent pas. Mais vous pouvez indiquer qu'une variable est une constante et ne doit pas être modifiée en ajoutant CONST_ ou CONSTANT_ au début du nom de la variable ou en nommant la variable dans BLOCK CAPITALS et en indiquant qu'il s'agit d'une constante. dans un commentaire:

    myVariable = 0
    CONST_daysInWeek = 7    # This is a constant - do not change its value.   
    CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.
2
Aryan Beezadhur

Vous pouvez le faire avec collections.namedtuple et itertools:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
2
Jaunty Sankey

(Ce paragraphe était censé être un commentaire sur ces réponses ici et , qui mentionnait namedtuple, mais cela prend trop de temps pour être inséré dans un commentaire. , alors, ça y est.)

L'approche namedtuple mentionnée ci-dessus est définitivement novatrice. Par souci d’exhaustivité, cependant, à la fin de la section NamedTuple de sa documentation officielle , on lit:

les constantes énumérées peuvent être implémentées avec des tuples nommés, mais il est plus simple et plus efficace d'utiliser une déclaration de classe simple:

class Status:
    open, pending, closed = range(3)

En d'autres termes, la documentation officielle préfère utiliser une méthode pratique plutôt que de mettre en œuvre le comportement en lecture seule. Je suppose que cela devient encore un autre exemple de Zen of Python :

Simple c'est mieux que complexe.

la praticité bat la pureté.

2
RayLuo

Dans mon cas, j'avais besoin de bytearrays immuables pour l'implémentation d'une bibliothèque de chiffrement contenant de nombreux nombres littéraux que je voulais assurer, ils étaient constants.

Cette réponse fonctionne, mais une tentative de réattribution d'éléments bytearray ne génère pas d'erreur.

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

Les constantes sont immuables, mais l'attribution constante bytearray échoue silencieusement:

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

Une approche plus puissante, simple et peut-être même plus 'Pythonic' implique l'utilisation d'objets memoryview (objets tampons dans <= python-2.6).

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    Elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

L'affectation d'élément ConstArray est une TypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222
1
jxqz

Vous pouvez envelopper une constante dans un tableau numpy, la marquer uniquement en écriture et l'appeler toujours par l'index zéro.

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

bien sûr, cela ne protège que le contenu de numpy, pas la variable "CONSTANT" elle-même; vous pouvez toujours faire:

CONSTANT = 'foo'

et CONSTANT changerait, mais cela provoquerait rapidement une erreur TypeError lors du premier appel de CONSTANT[0] dans le script.

bien que ... je suppose que si à un moment donné vous changiez cela

CONSTANT = [1,2,3]

maintenant vous n'obtiendrez plus le TypeError. hmmmm ....

https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html

1
litepresence

J'écris une bibliothèque util pour python const: kkconst - pypi support str, int, float, datetime

l'instance de champ const conservera son comportement de type base.

Par exemple:

from __future__ import print_function
from kkconst import (
    BaseConst,
    ConstFloatField,
)

class MathConst(BaseConst):
    PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
    E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")  # Euler's number"
    GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")

magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)

print(magic_num)  # 0.6180339887
print(magic_num.verbose_name)  # Golden Ratio

plus de détails sur l'utilisation, vous pouvez lire l'URL de pypi: pypi ou github

1
kaka_ace

Vous pouvez émuler des variables constantes à l'aide de la classe suivante. Un exemple d'utilisation:

# Const
const = Const().add(two=2, three=3)

print 'const.two: ', const.two
print 'const.three: ', const.three

const.add(four=4)

print 'const.four: ', const.four

#const.four = 5 # a error here: four is a constant

const.add(six=6)

print 'const.six: ', const.six

const2 = Const().add(five=5) # creating a new namespace with Const()
print 'const2.five: ', const2.five
#print 'const2.four: ', const2.four # a error here: four does not exist in const2 namespace

const2.add(five=26)

Appelez le constructeur lorsque vous souhaitez démarrer un nouvel espace de nom constant. Notez que la classe est sous protection contre les modifications inattendues des constantes de type séquence lorsque la classe de Martelli const n'est pas.

La source est ci-dessous.

from copy import copy

class Const(object):
"A class to create objects with constant fields."

def __init__(self):
    object.__setattr__(self, '_names', [])


def add(self, **nameVals):
    for name, val in nameVals.iteritems():          
        if hasattr(self, name):
            raise ConstError('A field with a name \'%s\' is already exist in Const class.' % name)

        setattr(self, name, copy(val)) # set up getter

        self._names.append(name)

    return self


def __setattr__(self, name, val):
    if name in self._names:
        raise ConstError('You cannot change a value of a stored constant.')

    object.__setattr__(self, name, val)
0
sergzach

Toutes les autres réponses ont raison. Python n'a pas le concept de "variable constante", mais vous pouvez émuler cette fonctionnalité à l'aide de classes définies par l'utilisateur avec une méthode surchargée __setattr__. Cependant, à partir de python3.4 +, ce processus devient beaucoup plus simple et plus facile à spécifier grâce à notre introduction à types énumérables, ou Enum qui correspondent à la définition d'une "constante" au sens le plus réel:

from enum import Enum
import math

class Constants(Enum):
    PI = math.pi
    EXP = math.e
    GOLDEN_RATIO = (1 + 5 ** 0.5) / 2

La valeur réelle est accessible à l’aide de l’attribut value.

Constants.PI.value
#  3.141592653589793

Cette valeur ne peut être ni modifiée ni réaffectée.

Constants.PI = 123
# AttributeError: Cannot reassign members.

Voir le documentation pour plus d'informations.

0
cs95

Ici, c’est un recueil d’idiomes que j’ai créé pour tenter d’améliorer certaines des réponses déjà disponibles.

Je sais que l'utilisation de constant n'est pas Pythonic et vous ne devriez pas le faire chez vous!

Cependant, Python est un langage tellement dynamique! Ce forum montre comment il est possible de créer des constructions qui ressemblent et se sentent comme des constantes. Cette réponse a pour objectif principal d’explorer ce qui peut être exprimé par la langue.

S'il vous plaît ne soyez pas trop dur avec moi :-).

Pour plus de détails, j'ai écrit un blog d'accompagnement sur ces idiomes .

Dans cet article, je vais appeler une variable constante une référence constante à des valeurs (immuables ou non). De plus, je dis qu'une variable a une valeur figée lorsqu'elle fait référence à un objet mutable pour lequel un code client ne peut pas mettre à jour sa ou ses valeurs.

Un espace de constantes (SpaceConstants)

Cet idiome crée ce qui ressemble à un espace de noms de variables constantes (a.k.a. SpaceConstants). Il s'agit d'une modification d'un extrait de code effectuée par Alex Martelli afin d'éviter l'utilisation d'objets module. En particulier, cette modification utilise ce que j’appelle une fabrique de classes car dans la fonction SpaceConstants , une classe appelée SpaceConstants , et une une instance est retournée.

J'ai exploré l'utilisation de fabrique de classes pour mettre en œuvre un sosie de conception basé sur des règles dans Python dans stackoverflow et également dans un blogpost .

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

Un espace de valeurs figées (SpaceFrozenValues)

Cet idiome suivant est une modification du SpaceConstants dans lequel les objets mutables référencés sont gelés. Cette implémentation exploite ce que j'appelle fermeture partagée entre setattr et getattr fonctions. La valeur de l'objet mutable est copiée et référencée par la variable cache définir à l'intérieur de la fermeture partagée de la fonction. Cela forme ce que j'appelle une copie protégée de la fermeture d'un objet mutable .

Vous devez faire preuve de prudence lorsque vous utilisez cet idiome car getattr renvoie la valeur de cache en effectuant une copie complète. Cette opération pourrait avoir un impact significatif sur les performances des objets volumineux!

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"

Un espace constant (ConstantSpace)

Cet idiome est un espace de noms immuable de variables constantes ou ConstantSpace . C'est une combinaison de la réponse extrêmement simple de Jon Betts dans stackoverflow avec une classe fabrique .

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"

Un espace gelé (FrozenSpace)

Cet idiome est un espace de noms immuable de variables gelées ou FrozenSpace . Il est dérivé du modèle précédent en transformant chaque variable en une classe propriété protégée par fermeture de la classe générée FrozenSpace .

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
0
Victor Bazterra

Parmi toutes les réponses fournies, voici le moyen le plus simple de créer une variable constante en Python. Il suffit de faire une seule dimension Tuple.

myconstant_var = (10,)

C'est ça. Maintenant, la variable myconstant_var ne peut pas être changée

0
Heapify

Étendant la réponse de Raufio, ajoutez un __repr__ pour renvoyer la valeur.

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)
    def __repr__(self):
        return ('{0}'.format(self.value))

dt = const(float(0.01))
print dt

alors l'objet se comporte un peu plus comme on pourrait s'y attendre, vous pouvez y accéder directement plutôt que '.value'

bien .. même si c'est obsolète, laissez-moi ajouter mes 2 centimes ici :-)

class ConstDict(dict):
    def __init__(self, *args, **kwargs):
        super(ConstDict, self).__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if key in self:
            raise ValueError("Value %s already exists" % (key))
        super(ConstDict, self).__setitem__(key, value)

Au lieu de ValueError, vous pouvez empêcher toute mise à jour de s'y produire. Un des avantages est que vous pouvez ajouter des constantes de manière dynamique dans le programme, mais que vous ne pouvez pas modifier une fois qu'une constante est définie. Vous pouvez aussi ajouter n'importe quelle règle avant de définir une constante (une clé, par exemple, doit être une chaîne, une chaîne en minuscule ou une chaîne en majuscule, etc. avant de définir la clé).

Cependant, je ne vois aucune importance à définir des constantes en Python. Aucune optimisation ne peut se produire comme en C et c'est donc quelque chose d'inutile, je suppose.

0
thiruvenkadam