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?
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.
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:
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 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.
property
pour construire un objet pouvant être "défini" ou "obtenu".property
sont nommés fset
et fget
.property
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
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.
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).
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. :-)
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.
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.
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
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:
Testé avec Python3.4 et Python3.5 (je suis utiliser le "tox" pour cela)
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
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").
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.
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.
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
.
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.
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
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}
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.
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.
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
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)
Simplement vous pouvez simplement:
STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89
espère que cela rend tout beaucoup plus simple
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.
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.
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
(Ce paragraphe était censé être un commentaire sur ces réponses ici et là , 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é.
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
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
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
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)
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.
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.
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"
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"
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'"
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'"
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
É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.