En realidad, la forma de hacer variables de solo lectura es posible a través de la función / decorador de propiedades de Python . la respuesta de inv es un ejemplo de un uso personalizado de eso. Sin embargo, la propiedad es de uso más general que eso, un buen análisis de cómo funciona es en los atributos y métodos de Python de Shalabh Chaturvedi .
n611x007
20
En mi humilde opinión, hacer cumplir la constancia "no es pitónico". En Python 2.7 incluso puedes escribir True=False, y luego (2+2==4)==Trueregresa False.
osa
8
Como otras respuestas sugieren, no hay forma o no es necesario declarar constantes. Pero puede leer este PEP sobre convenciones. eg THIS_IS_A_CONSTANT
Rasika Perera
34
@osa: No puedes hacer eso en Python 3 - SyntaxError: can't assign to keyword. Esto parece una buena cosa.
naught101
3
Sorprendido, esto no se ha mencionado hasta ahora, pero Enums parece una buena forma de definir constantes enumeradas.
cs95
Respuestas:
973
No no hay. No puede declarar una variable o valor como constante en Python. Simplemente no lo cambies.
Si estás en una clase, el equivalente sería:
classFoo(object):
CONST_NAME ="Name"
si no, es solo
CONST_NAME ="Name"
Pero es posible que desee ver el fragmento de código Constantes en Python de Alex Martelli.
A partir de Python 3.8, hay una typing.Finalanotación variable que indicará a los verificadores de tipo estático (como mypy) que su variable no debe reasignarse. Este es el equivalente más cercano al de Java final. Sin embargo, en realidad no evita la reasignación :
from typing importFinal
a:Final=1# Executes fine, but mypy will report an error if you run mypy on this:
a =2
En lugar de hacer lo que está en "Constantes en Python", debe usar la función o decorador de "propiedad".
Seth Johnson
26
La gente pregunta sobre la misma característica en Perl. Hay un módulo de importación llamado "use constant", pero (AFAIK) es solo un contenedor para crear una pequeña función que devuelve el valor. Yo hago lo mismo en Python. Ejemplo:def MY_CONST_VALUE(): return 123
kevinarpe
8
"No no hay." Es cierto, pero basándose en el trabajo de otras personas, he agregado una respuesta, muy por debajo, con una implementación corta y simple de "Constantes" para python 2.7 (que carece de "enumeración"). Estos son de tipo enumático de solo lectura name.attributey pueden contener cualquier valor. La declaración es fácil Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), el uso es sencillo print 10 + Nums.PI, intenta cambiar los resultados en excepción Nums.PI = 22=> ValueError (..)
ToolmakerSteve
110
Simplemente no lo cambies. que hizo que mi día
Hi-Ángel
89
"Simplemente no lo cambie" no es útil en absoluto. No responde la pregunta y sugeriría que se elimine.
Bartek Banachewicz
354
No existe una constpalabra clave como en otros idiomas, sin embargo, es posible crear una Propiedad que tenga una "función de obtención" para leer los datos, pero no una "función de establecimiento" para volver a escribir los datos. Esto esencialmente protege el identificador de ser cambiado.
Aquí hay una implementación alternativa usando la propiedad de clase:
Tenga en cuenta que el código está lejos de ser fácil para un lector que se pregunta acerca de las constantes. Ver explicación abajo
Tenga en cuenta que el decorador @apply parece estar en desuso.
Para definir el identificador FOO, los primeros definen dos funciones (fset, fget: los nombres son a mi elección).
Luego use la propertyfunción incorporada para construir un objeto que pueda "establecerse" o "obtenerse".
Tenga en cuenta que los propertydos primeros parámetros de la función se denominan fsety fget.
Use el hecho de que elegimos estos nombres para nuestro propio captador y definidor y cree un diccionario de palabras clave usando el ** (doble asterisco) aplicado a todas las definiciones locales de ese alcance para pasar parámetros a la propertyfunción
Basado en la documentación sobre AttributeErrory TypeError, creo que la excepción planteada debería ser un nuevo error, que propongo nombrar ConstantErroro algo así, que es una subclase de TypeError. La sección en los documentos que me hace pensar que: docs.python.org/2/library/exceptions.html
ArtOfWarfare
3
Estoy sorprendido con este código. ¿Por qué la fuente de los métodos FOO () y BAR () tiene self como argumento? Mi IDE subraya los corchetes en rojo (error de "compilación"). Me cansé de ponerme, pero luego recibí un error.
user3770060
10
Llegar a estas longitudes esboza una clara deficiencia en el lenguaje python. ¿Por qué no sintieron la necesidad de agregar esto a Python 3. No puedo creer que nadie lo sugirió y simplemente no puedo ver la lógica detrás de algún comité que dice 'no, constantes? nah.
Andrew S
8
Y su solución aún puede ser modificado por un programador de Python determinado mediante el usoCONST.__dict__['FOO'] = 7
pppery
11
@OscarSmith, creo que mejoraría el diseño del 'código auto documentado'. Cuando hago explícito en el código que algún valor no puede cambiar, es más fácil de entender que leer todo el código fuente y darme cuenta de que algún valor nunca cambia. Además, bloquea la posibilidad de que alguien cambie un valor que debería ser, bueno, constante. Recuerde: explícito es mejor que implícito.
Gabriel
112
En Python en lugar de que el lenguaje imponga algo, las personas usan convenciones de nomenclatura, por ejemplo, __methodpara métodos privados y _methodpara métodos protegidos.
Entonces, de la misma manera, simplemente puede declarar la constante como mayúsculas, por ejemplo
MY_CONSTANT ="one"
Si desea que esta constante nunca cambie, puede engancharse al acceso a atributos y hacer trucos, pero un enfoque más simple es declarar una función
def MY_CONSTANT():return"one"
El único problema es en todas partes donde tendrá que hacer MY_CONSTANT (), pero nuevamente MY_CONSTANT = "one"es la forma correcta en python (generalmente).
También puede usar namedtuple para crear constantes:
>>>from collections import namedtuple>>>Constants= namedtuple('Constants',['pi','e'])>>> constants =Constants(3.14,2.718)>>> constants.pi3.14>>> constants.pi =3Traceback(most recent call last):File"<stdin>", line 1,in<module>AttributeError: can't set attribute
Hacer def MY_CONSTANT(): return "one"no detendrá a alguien, más adelante en el código, haciendo MY_CONSTANT = "two"(o redeclarando la función).
Matthew Schinckel
66
@MatthewSchinckel se trata de una convención, también cambiar MY_CONSTANT no cambiará el uso MY_CONSTANT () pero arrojará un error, y en Python si quieres puedes cambiar cualquier cosa, ningún truco inteligente puede protegerte.
Anurag Uniyal
3
Gracias por mencionar el enfoque de tupla nombrada. Definitivamente innovador. También puede encontrar mi "comentario" aquí relevante.
RayLuo
@MatthewSchinckel puedes redefinir CUALQUIER COSA en Python, así que ese no es realmente un buen punto.
cslotty
@MatthewSchinckel La idea no es escribir MY_CONSTANT = MY_CONSTANT(), sino usar MY_CONSTANT()como la constante. Por supuesto esto. Pero esto está bien y está muy en línea con el principio de Python "Todos somos adultos aquí", es decir, al desarrollador rara vez se le prohibirá decidir anular una regla cuando tenga buenas razones y sepa lo que está haciendo.
jonathan.scholbach
69
Recientemente encontré una actualización muy sucinta de esto que automáticamente genera mensajes de error significativos y evita el acceso a través de __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'
Nos definimos sobre nosotros mismos para convertirnos en una instancia y luego usamos espacios para asegurarnos de que no se puedan agregar atributos adicionales. Esto también elimina la __dict__ruta de acceso. Por supuesto, todo el objeto todavía se puede redefinir.
Editar - Solución original
Probablemente me estoy perdiendo un truco aquí, pero esto parece funcionar para mí:
La creación de la instancia permite que el __setattr__método mágico intervenga e intercepte los intentos de establecer la FOOvariable. Podrías lanzar una excepción aquí si quisieras. Instanciar la instancia sobre el nombre de la clase evita el acceso directamente a través de la clase.
Es un dolor total por un valor, pero puede adjuntar lotes a su CONSTobjeto. Tener una clase alta, el nombre de la clase también parece un poco sucio, pero creo que es bastante sucinto en general.
Esta es la mejor y más clara respuesta, porque tiene el menor "mecanismo", pero la mayor funcionalidad. Sin embargo, es importante plantear una excepción ... no es una opción.
Erik Aronesty
He elaborado una ruta más corta que automáticamente produce errores significativos, pero tiene el mismo estilo. He dejado la idea original aquí para comparar.
Jon Betts
Qué pena que aún necesites este CONST.prefijo. También en situaciones de módulos múltiples esto será complicado.
Alfe
1
Creo que normalmente querría agrupar constantes en algunos paquetes relacionados de todos modos en esa situación (en lugar de tener un objeto CONST gigante), por lo que probablemente no sea tan malo.
Jon Betts
¿Por qué esta respuesta todavía está tan lejos? La __slots__solución es tan elegante y efectiva. De todo lo que he leído, esto es lo más parecido a crear constantes en Python. Muchas gracias. Y para todos los interesados, aquí hay una explicación brillante y profunda de la __slots__magia.
JohnGalt
34
Python no tiene constantes.
Quizás la alternativa más fácil es definir una función para ello:
def MY_CONSTANT():return42
MY_CONSTANT() ahora tiene toda la funcionalidad de una constante (además de algunos aparatos molestos).
Solo quería agregar esta sugerencia, pero afortunadamente me desplacé a las respuestas de baja calificación. Espero que sea más votado y estoy totalmente de acuerdo en que tiene toda la funcionalidad de una constante y es muy simple y directo. Al observar la cantidad de código repetitivo en todas las soluciones sofisticadas, encuentro que los aparatos son relativamente molestos.
yaccob
1
Esta es la respuesta más simple, aunque debe tenerse en cuenta que tiene algunos gastos generales y no detendrá a los idiotas que modifican el valor de retorno. Solo evitará que el código más adelante cambie la fuente
MrMesees
@MrMesees modificando el valor de retorno? ¿Te refieres a editar la fuente? Pero de esto no estás protegido incluso en C ++, donde las constantes (como constexpr) son constantes realmente duras.
Ruslan
@Ruslan lo que quise decir es que como python no tiene constexpr, no detendría el valor que se está editando después de que se devuelva a un contexto externo. No se ha hecho nada a 42 para hacer cumplir el estado congelado en este ejemplo.
MrMesees
20
Además de las dos respuestas principales (solo use variables con nombres MAYÚSCULAS, o use propiedades para hacer que los valores sean de solo lectura), quiero mencionar que es posible usar metaclases para implementar constantes con nombre . Proporciono una solución muy simple usando metaclases en GitHub que puede ser útil si desea que los valores sean más informativos sobre su tipo / nombre:
>>>from named_constants importConstants>>>classColors(Constants):... black =0... red =1... white =15...>>> c =Colors.black
>>> c ==0True>>> c
Colors.black
>>> c.name()'black'>>>Colors(0)is c
True
Este es Python un poco más avanzado, pero sigue siendo muy fácil de usar y práctico. (El módulo tiene algunas características más, incluidas las constantes de solo lectura, consulte su archivo README).
Hay soluciones similares que flotan en varios repositorios, pero que yo sepa carecen de una de las características fundamentales que esperaría de las constantes (como ser constante o de tipo arbitrario), o tienen características esotéricas agregadas que hacerlos menos aplicables en general. Pero YMMV, agradecería sus comentarios. :-)
Me gusta tu implementación en GitHub. Estaba casi listo para escribir una clase básica que implementara la funcionalidad de búsqueda inversa, ¡pero veo que lo has hecho y más!
Kerr
Gracias, @Kerr, es el primer comentario que recibí y me hizo feliz. :-)
hans_meine
Increíble. Acabo de probar esto. Es bueno tener esto como opción. Aunque no he decidido si me importa lo suficiente el aspecto de solo lectura, usar esto en lugar de simplemente hacerlo def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), según stackoverflow.com/a/1695250/199364 , sección "En versiones anteriores ..."
ToolmakerSteve
19
Las propiedades son una forma de crear constantes. Puede hacerlo declarando una propiedad getter, pero ignorando el setter. Por ejemplo:
Solución infravalorada. Acabo de implementar esto después de encontrar esta página (no esta respuesta) y volví en círculo para agregarla si no es así. Quería subrayar la utilidad de esta respuesta.
Marc
18
Editar: Código de muestra agregado para Python 3
Nota: esta otra respuesta parece que proporciona una implementación mucho más completa, similar a la siguiente (con más funciones).
Puede usar una tupla con nombre como solución alternativa para crear efectivamente una constante que funcione de la misma manera que una variable final estática en Java (una "constante" de Java). A medida que avanzan las soluciones, es algo elegante. (Un enfoque más elegante sería simplemente mejorar el lenguaje Python --- ¿qué tipo de lenguaje te permite redefinir math.pi? - pero estoy divagando).
(Mientras escribo esto, me doy cuenta de que otra respuesta a esta pregunta mencionó namedtuple, pero continuaré aquí porque mostraré una sintaxis que se asemeja más a lo que esperarías en Java, ya que no hay necesidad de crear un named escribir como namedtuple te obliga a hacerlo).
Siguiendo su ejemplo, recordará que en Java debemos definir la constante dentro de alguna clase ; porque no mencionaste un nombre de clase, llamémoslo Foo. Aquí está la clase de Java:
public classFoo{
public static final String CONST_NAME ="Name";}
Aquí está el Python equivalente.
from collections import namedtuple
Foo= namedtuple('_Foo','CONST_NAME')('Name')
El punto clave que quiero agregar aquí es que no necesita un Footipo separado (una "tupla anónima llamada sería bueno, aunque eso suene como un oxímoron), así que llamamos a nuestra tupla nombrada _Foopara que con suerte no lo haga escapar a la importación de módulos.
El segundo punto aquí es que creamos inmediatamente una instancia de la nametuple, llamándola Foo; no es necesario hacer esto en un paso separado (a menos que lo desee). Ahora puedes hacer lo que puedes hacer en Java:
>>>Foo.CONST_NAME
'Name'
Pero no puedes asignarle:
>>>Foo.CONST_NAME ='bar'…AttributeError: can't set attribute
Reconocimiento: pensé que inventé el enfoque de tupla nombrada, pero luego veo que alguien más dio una respuesta similar (aunque menos compacta). Entonces también noté ¿Qué son las "tuplas con nombre" en Python? , que señala que sys.version_infoahora es una tupla con nombre, por lo que quizás la biblioteca estándar de Python ya tuvo esta idea mucho antes.
Tenga en cuenta que desafortunadamente (esto sigue siendo Python), puede borrar toda la Footarea por completo:
>>>Foo='bar'
(facepalm)
Pero al menos estamos evitando que Foo.CONST_NAMEse cambie el valor, y eso es mejor que nada. Buena suerte.
Aquí hay una implementación de una clase "Constantes", que crea instancias con atributos de solo lectura (constantes). Por ejemplo, puede usar Nums.PIpara obtener un valor que se ha inicializado como 3.14159, y Nums.PI = 22genera una excepción.
# ---------- Constants.py ----------classConstants(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:raiseValueError("setattr while locked", self)if(__name__ =="__main__"):# Usage example.Nums=Constants(ONE=1, PI=3.14159,DefaultWidth=100.0)print10+Nums.PI
print'----- Following line is deliberate ValueError -----'Nums.PI =22
Gracias a FrozenDict de @MikeGraham , que utilicé como punto de partida. Cambiado, por lo que en lugar de Nums['ONE']la sintaxis de uso es Nums.ONE.
Y gracias a la respuesta de @ Raufio, por la idea de anular __ setattr __.
Python es un lenguaje de adultos que consienten. No hay protección contra algo como esto. Nums._d['PI'] = 22 El lenguaje en sí no proporciona ninguna forma de marcar las cosas como no mutables, creo.
Ajay M
8
Una tupla técnicamente califica como una constante, ya que una tupla generará un error si intenta cambiar uno de sus valores. Si desea declarar una tupla con un valor, coloque una coma después de su único valor, así:
my_tuple =(0"""Or any other value""",)
Para verificar el valor de esta variable, use algo similar a esto:
if my_tuple[0]==0:#Code goes here
Si intenta cambiar este valor, se generará un error.
Haría una clase que anule el __setattr__método de la clase de objeto base y ajuste mis constantes con eso, tenga en cuenta que estoy usando python 2.7:
class const(object):def __init__(self, val):
super(const, self).__setattr__("value", val)def __setattr__(self, name, val):raiseValueError("Trying to change a constant value", self)
Para envolver una cadena:
>>> 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'
Es bastante simple, pero si desea usar sus constantes de la misma manera que lo haría con un objeto no constante (sin usar constObj.value), será un poco más intenso. Es posible que esto pueda causar problemas, por lo que podría ser mejor mantener el .valuepara mostrar y saber que está haciendo operaciones con constantes (aunque quizás no sea la forma más 'pitónica').
+1 para un enfoque interesante. Aunque no tan limpio como las respuestas que ya se habían proporcionado. E incluso la solución def ONE(): return 1más simple sugerida anteriormente es más fácil de usar ONE()que esta respuesta ONE.value.
ToolmakerSteve
7
Desafortunadamente, Python aún no tiene constantes y es una pena. ES6 ya agregó constantes de soporte a JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ) ya que es algo muy útil en cualquier lenguaje de programación. Como se respondió en otras respuestas en la comunidad de Python, use la convención: la variable en mayúsculas del usuario como constantes, pero no protege contra errores arbitrarios en el código. Si lo desea, puede que le resulte útil una solución de un solo archivo como la siguiente (vea las cadenas de documentos sobre cómo usarla).
archivo constantes.py
import collections
__all__ =('const',)classConstant(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__:raiseTypeError('Constanst can not be changed')ifnot isinstance(value, collections.Hashable):if isinstance(value, list):
value = tuple(value)elif isinstance(value, set):
value = frozenset(value)elif isinstance(value, dict):raiseTypeError('dict can not be used as constant')else:raiseValueError('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__:raiseTypeError('Constanst can not be deleted')raiseNameError("name '%s' is not defined"% name)def __call__(self):"""Return all constans."""return self.__dict__
const =Constant()if __name__ =='__main__':import doctest
doctest.testmod()
Si esto no es suficiente, vea el caso de prueba completo.
import decimal
import uuid
import datetime
import unittest
from..constants importConstantclassTestConstant(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 =.211def 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 =.211def 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 =.211def 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 =.211def 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 =Falsedef 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 =3212def 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 =Truedef 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 =Truedef 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 =Nonedef 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 =Nonedef 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 =1def 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"})
Ventajas: 1. Acceso a todas las constantes para todo el proyecto 2. Control estricto para valores de constantes
Carece: 1. No es compatible con los tipos personalizados y el tipo 'dict'
Notas:
Probado con Python3.4 y Python3.5 (estoy usando el 'tox' para ello)
Entorno de prueba:
.
$ uname -a
Linux wlysenko-Aspire3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Podría mejorar esto ligeramente al convertir automáticamente los diccionarios a tuplas con nombre
Peter Schorn
6
La forma pitónica de declarar "constantes" es básicamente una variable de nivel de módulo:
RED =1
GREEN =2
BLUE =3
Y luego escribe tus clases o funciones. Dado que las constantes son casi siempre números enteros, y también son inmutables en Python, tiene muy pocas posibilidades de alterarlo.
A menos, por supuesto, si establece explícitamente RED = 2.
Sí, pero bloquear la capacidad de "establecer explícitamente RED = 2" es el beneficio completo (en otros idiomas) de poder declarar un nombre de variable como "constante".
ToolmakerSteve
66
¿Te beneficiarías de bloquear eso? Lo más útil sobre const es generalmente las optimizaciones del compilador, lo que no es realmente algo en Python. ¿Quieres que algo sea constante? Simplemente no lo cambies. Si te preocupa que alguien más lo cambie, puedes ponerlo fuera de su alcance, o simplemente darte cuenta de que, si alguien lo está cambiando, ese es su problema y tienen que lidiar con eso, no tú.
Kevin
@Kevin: " ¿Te beneficiarías de obtener ... ", el beneficio de statictener un único almacenamiento para el valor de todas las instancias de una clase? A menos que exista la posibilidad de declarar una variable estática / clase de hecho.
minutos
8
El problema raíz es que algunos pueden verlo como un valor que es una fuente de verdad, no se puede cambiar, y usarlo como la fuente de verdad en todo su código en lugar de introducir valores mágicos (que veo mucho en Python) - y otros pueden verlo como algo que pueden cambiar a voluntad. Cuando alguien cambia una variable global, y no puede saber dónde se modificó, y la aplicación se bloquea porque RED = "azul" en lugar de "rojo", está introduciendo un problema totalmente innecesario que ya se ha resuelto de manera tan simple y se entiende universalmente
Dagrooms
5
Podemos crear un objeto descriptor.
classConstant:def __init__(self,value=None):
self.value = value
def __get__(self,instance,owner):return self.value
def __set__(self,instance,value):raiseValueError("You can't change a constant")
1) Si quisiéramos trabajar con constantes a nivel de instancia, entonces:
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 =100Traceback(most recent call last):File"<stdin>", line 1,in<module>ValueError:You can't change a constant
2) si quisiéramos crear constantes solo a nivel de clase, podríamos usar una metaclase que sirva como contenedor para nuestras constantes (nuestros objetos descriptores); Todas las clases que descienden heredarán nuestras constantes (nuestros objetos descriptores) sin ningún riesgo que pueda modificarse.
# metaclass of my class FooclassFooMeta(type):pass# class FooclassFoo(metaclass=FooMeta):pass# I create constants in my metaclassFooMeta.NUM =Constant(0xff)FooMeta.NAME =Constant('FOO')>>>Foo.NUM #=> 255>>>Foo.NAME #=> 'FOO'>>>Foo.NUM =0#=> ValueError: You can't change a constant
Si creo una subclase de Foo, esta clase heredará la constante sin la posibilidad de modificarla
classBar(Foo):pass>>>Bar.NUM #=> 255>>>Bar.NUM =0#=> ValueError: You can't change a constant
En python, una constante es simplemente una variable con un nombre en mayúsculas, con palabras separadas por el carácter de subrayado,
p.ej
DAYS_IN_WEEK = 7
El valor es mutable, ya que puede cambiarlo. Pero dadas las reglas para que el nombre te diga que es una constante, ¿por qué lo harías? Quiero decir, ¡es tu programa después de todo!
Este es el enfoque adoptado en Python. No hay privatepalabra clave por la misma razón. Prefije el nombre con un guión bajo y sabrá que está destinado a ser privado. El código puede romper la regla ... así como un programador podría eliminar la palabra clave privada de todos modos.
Python podría haber agregado una constpalabra clave ... pero un programador podría eliminar la palabra clave y luego cambiar la constante si así lo desean, pero ¿por qué hacer eso? Si desea romper la regla, puede cambiarla de todos modos. Pero, ¿por qué molestarse en romper la regla si el nombre deja clara la intención?
¿Quizás haya alguna prueba unitaria en la que tenga sentido aplicar un cambio de valor? Para ver qué sucede durante una semana de 8 días, aunque en el mundo real no se puede cambiar el número de días de la semana. Si el lenguaje lo detuvo haciendo una excepción si solo hay un caso en el que necesita romper la regla ... entonces tendría que dejar de declararlo como una constante, a pesar de que todavía es una constante en la aplicación, y hay solo este caso de prueba que ve lo que sucede si se cambia.
El nombre en mayúsculas le dice que está destinado a ser una constante. Eso es lo importante. No es un idioma que imponga restricciones en el código que tiene el poder de cambiar de todos modos.
No hay una manera perfecta de hacer esto. Según tengo entendido, la mayoría de los programadores simplemente capitalizarán el identificador, por lo que PI = 3.142 puede entenderse fácilmente como una constante.
Por otro lado, si quieres algo que realmente actúe como una constante, no estoy seguro de que lo encuentres. Con todo lo que hagas siempre habrá alguna forma de editar la "constante", por lo que realmente no será una constante. Aquí hay un ejemplo muy simple y sucio:
En realidad, todo lo que se necesita para que alguien cambie el valor es esto:
globals()["PI"+str(id("PI"))]=3.1415
Esto es lo mismo para todas las otras soluciones que encontrará aquí, incluso las inteligentes que hacen una clase y redefinen el método de atributo establecido, siempre habrá una forma de evitarlas. Así es como es Python.
Mi recomendación es evitar todas las molestias y capitalizar sus identificadores. Realmente no sería una constante adecuada, pero de nuevo nada lo haría.
Para todos los que están leyendo esto, tenga en cuenta que, si establece un objeto mutable como una de estas constantes, cualquiera puede alterar su valor interno. por ejemplo, vamos a bar = [1, 2, 3], entonces, podría hacer lo siguiente: CONSTS.bar [1] = 'a' y no será rechazado. Así que ten cuidado con esto.
Juan Ignacio Sánchez
En lugar de este método hacky, que hice solo por diversión, recomiendo usar el decorador de propiedades de Python.
Juan Ignacio Sánchez
4
Tal vez la biblioteca pconst te ayudará ( 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.
(Este párrafo estaba destinado a ser un comentario sobre esas respuestas aquí y allá , que mencionó namedtuple, pero se está haciendo demasiado largo para que quepa en un comentario, así que aquí va).
El enfoque de doble nombre mencionado anteriormente es definitivamente innovador. Sin embargo, en aras de la exhaustividad, al final de la sección NamedTuple de su documentación oficial , se lee:
las constantes enumeradas se pueden implementar con tuplas con nombre, pero es más simple y más eficiente usar una declaración de clase simple:
classStatus:
open, pending, closed = range(3)
En otras palabras, la documentación oficial prefiere usar una forma práctica, en lugar de implementar realmente el comportamiento de solo lectura. Supongo que se convierte en otro ejemplo más de Zen de Python :
Aquí hay una colección de modismos que creé como un intento de mejorar algunas de las respuestas ya disponibles.
Sé que el uso de la constante no es pitónico, ¡y no debes hacerlo en casa!
¡Sin embargo, Python es un lenguaje tan dinámico! Este foro muestra cómo es posible la creación de construcciones que se vean y se sientan como constantes. Esta respuesta tiene como objetivo principal explorar lo que el lenguaje puede expresar.
En esta publicación, llamaré una variable constante a una referencia constante a valores (inmutables o no). Además, digo que una variable tiene un valor congelado cuando hace referencia a un objeto mutable que un código de cliente no puede actualizar su (s) valor (es).
Un espacio de constantes (SpaceConstants)
Este modismo crea lo que parece un espacio de nombres de variables constantes (también conocido como SpaceConstants). Es una modificación de un fragmento de código de Alex Martelli para evitar el uso de objetos de módulo. En particular, esta modificación usa lo que yo llamo una fábrica de clases porque dentro de la función SpaceConstants , se define una clase llamada SpaceConstants y se devuelve una instancia.
Exploré el uso de class factory para implementar un diseño similar basado en políticas en Python en stackoverflow y también en una publicación de blog .
Un espacio de valores congelados (SpaceFrozenValues)
Este siguiente modismo es una modificación de los SpaceConstants en donde los objetos mutables a los que se hace referencia están congelados. Esta implementación explota lo que llamo cierre compartido entre las funciones setattr y getattr . El valor del objeto mutable es copiado y referenciado por el caché variable definido dentro del cierre compartido de la función. Forma lo que yo llamo una copia protegida de cierre de un objeto mutable .
Debe tener cuidado al usar este modismo porque getattr devuelve el valor de caché haciendo una copia profunda. ¡Esta operación podría tener un impacto significativo en el rendimiento de objetos grandes!
from copy import deepcopy
defSpaceFrozenValues():
cache ={}def setattr(self, name, value):nonlocal cache
if name in cache:raiseAttributeError("Cannot reassign members")
cache[name]= deepcopy(value)def getattr(self, name):nonlocal cache
if name notin cache:raiseAttributeError("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 xprint(fv.x)# print "2"
fv.x =3# raise "AttributeError: Cannot reassign members"
fv.y ={'name':'y','value':2}# bind attribute yprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y['name']='yprime'# you can try to change mutable objectsprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y ={}# raise "AttributeError: Cannot reassign members"
Un espacio constante (ConstantSpace)
Este idioma es un espacio de nombres inmutable de variables constantes o ConstantSpace . Es una combinación de la respuesta increíblemente simple de Jon Betts en stackoverflow con una fábrica de clase .
defConstantSpace(**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 changedprint(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 espacio congelado (FrozenSpace)
Este idioma es un espacio de nombres inmutable de variables congeladas o FrozenSpace . Se deriva del patrón anterior al hacer que cada variable sea una propiedad protegida al cerrar la clase FrozenSpace generada .
from copy import deepcopy
defFreezeProperty(value):
cache = deepcopy(value)return property(lambda self: deepcopy(cache))defFrozenSpace(**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 objectprint(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'"
En Python, las constantes no existen, pero puede indicar que una variable es una constante y no debe cambiarse agregando CONST_al inicio del nombre de la variable y declarando que es una constante en un comentario:
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.
Alternativamente, puede crear una función que actúe como una constante:
En mi caso, necesitaba bytearrays inmutables para la implementación de una biblioteca criptográfica que contiene muchos números literales que quería asegurar que fueran constantes.
Esta respuesta funciona, pero el intento de reasignación de elementos bytearray no genera un error.
def const(func):'''implement const decorator'''def fset(self, val):'''attempting to set a const raises `ConstError`'''classConstError(TypeError):'''special exception for const reassignment'''passraiseConstErrordef fget(self):'''get a const'''return func()return property(fget, fset)classConsts(object):'''contain all constants'''@constdef C1():'''reassignment to C1 fails silently'''return bytearray.fromhex('deadbeef')@constdef pi():'''is immutable'''return3.141592653589793
Las constantes son inmutables, pero la asignación constante de bytearray falla en silencio:
>>> 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
raiseConstError
__main__.ConstError>>> c.C1[0]=0>>> c.C1[0]222>>> c.C1
bytearray(b'\xde\xad\xbe\xef')
Un enfoque más poderoso, simple y quizás incluso más 'pitónico' implica el uso de objetos de memoria (objetos de memoria intermedia en <= python-2.6).
import sys
PY_VER = sys.version.split()[0].split('.')if int(PY_VER[0])==2:if int(PY_VER[1])<6:raiseNotImplementedErrorelif int(PY_VER[1])==6:
memoryview = buffer
classConstArray(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)
La asignación de elementos de ConstArray es un TypeError:
>>> C1 =ConstArray('deadbeef')>>> C1[0]=0Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError:'ConstArray' object does not support item assignment
>>> C1[0]222
Escribo una lib de utilidad para python const:
kkconst - pypi
support str, int, float, datetime
la instancia del campo const mantendrá su comportamiento de tipo base.
Por ejemplo:
from __future__ import print_function
from kkconst import(BaseConst,ConstFloatField,)classMathConst(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.6180339887print(magic_num.verbose_name)# Golden Ratio
más detalles de uso puedes leer la url de pypi :
pypi o github
Puede ajustar una constante en una matriz numpy, marcarla solo de escritura y siempre llamarla por índice cero.
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
por supuesto, esto solo protege el contenido del numpy, no la variable "CONSTANTE" en sí misma; aún puedes hacer:
CONSTANT ='foo'
y CONSTANTcambiaría, sin embargo, eso arrojaría rápidamente un TypeError la primera vez CONSTANT[0]que luego se llama en el script.
aunque ... supongo que si en algún momento lo cambiaste a
True=False
, y luego(2+2==4)==True
regresaFalse
.SyntaxError: can't assign to keyword
. Esto parece una buena cosa.Respuestas:
No no hay. No puede declarar una variable o valor como constante en Python. Simplemente no lo cambies.
Si estás en una clase, el equivalente sería:
si no, es solo
Pero es posible que desee ver el fragmento de código Constantes en Python de Alex Martelli.
A partir de Python 3.8, hay una
typing.Final
anotación variable que indicará a los verificadores de tipo estático (como mypy) que su variable no debe reasignarse. Este es el equivalente más cercano al de Javafinal
. Sin embargo, en realidad no evita la reasignación :fuente
def MY_CONST_VALUE(): return 123
name.attribute
y pueden contener cualquier valor. La declaración es fácilNums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
, el uso es sencilloprint 10 + Nums.PI
, intenta cambiar los resultados en excepciónNums.PI = 22
=> ValueError (..)No existe una
const
palabra clave como en otros idiomas, sin embargo, es posible crear una Propiedad que tenga una "función de obtención" para leer los datos, pero no una "función de establecimiento" para volver a escribir los datos. Esto esencialmente protege el identificador de ser cambiado.Aquí hay una implementación alternativa usando la propiedad de clase:
Tenga en cuenta que el código está lejos de ser fácil para un lector que se pregunta acerca de las constantes. Ver explicación abajo
Explicación del código:
constant
que tome una expresión y la use para construir un "captador", una función que únicamente devuelve el valor de la expresión.constant
función que acabamos de crear como decoración para definir rápidamente las propiedades de solo lectura.Y de alguna otra forma más anticuada:
(El código es bastante complicado, más explicaciones a continuación)
Tenga en cuenta que el decorador @apply parece estar en desuso.
property
función incorporada para construir un objeto que pueda "establecerse" o "obtenerse".property
dos primeros parámetros de la función se denominanfset
yfget
.property
funciónfuente
AttributeError
yTypeError
, creo que la excepción planteada debería ser un nuevo error, que propongo nombrarConstantError
o algo así, que es una subclase deTypeError
. La sección en los documentos que me hace pensar que: docs.python.org/2/library/exceptions.htmlCONST.__dict__['FOO'] = 7
En Python en lugar de que el lenguaje imponga algo, las personas usan convenciones de nomenclatura, por ejemplo,
__method
para métodos privados y_method
para métodos protegidos.Entonces, de la misma manera, simplemente puede declarar la constante como mayúsculas, por ejemplo
Si desea que esta constante nunca cambie, puede engancharse al acceso a atributos y hacer trucos, pero un enfoque más simple es declarar una función
El único problema es en todas partes donde tendrá que hacer MY_CONSTANT (), pero nuevamente
MY_CONSTANT = "one"
es la forma correcta en python (generalmente).También puede usar namedtuple para crear constantes:
fuente
def MY_CONSTANT(): return "one"
no detendrá a alguien, más adelante en el código, haciendoMY_CONSTANT = "two"
(o redeclarando la función).MY_CONSTANT = MY_CONSTANT()
, sino usarMY_CONSTANT()
como la constante. Por supuesto esto. Pero esto está bien y está muy en línea con el principio de Python "Todos somos adultos aquí", es decir, al desarrollador rara vez se le prohibirá decidir anular una regla cuando tenga buenas razones y sepa lo que está haciendo.Recientemente encontré una actualización muy sucinta de esto que automáticamente genera mensajes de error significativos y evita el acceso a través de
__dict__
:Nos definimos sobre nosotros mismos para convertirnos en una instancia y luego usamos espacios para asegurarnos de que no se puedan agregar atributos adicionales. Esto también elimina la
__dict__
ruta de acceso. Por supuesto, todo el objeto todavía se puede redefinir.Editar - Solución original
Probablemente me estoy perdiendo un truco aquí, pero esto parece funcionar para mí:
La creación de la instancia permite que el
__setattr__
método mágico intervenga e intercepte los intentos de establecer laFOO
variable. Podrías lanzar una excepción aquí si quisieras. Instanciar la instancia sobre el nombre de la clase evita el acceso directamente a través de la clase.Es un dolor total por un valor, pero puede adjuntar lotes a su
CONST
objeto. Tener una clase alta, el nombre de la clase también parece un poco sucio, pero creo que es bastante sucinto en general.fuente
CONST.
prefijo. También en situaciones de módulos múltiples esto será complicado.__slots__
solución es tan elegante y efectiva. De todo lo que he leído, esto es lo más parecido a crear constantes en Python. Muchas gracias. Y para todos los interesados, aquí hay una explicación brillante y profunda de la__slots__
magia.Python no tiene constantes.
Quizás la alternativa más fácil es definir una función para ello:
MY_CONSTANT()
ahora tiene toda la funcionalidad de una constante (además de algunos aparatos molestos).fuente
constexpr
) son constantes realmente duras.Además de las dos respuestas principales (solo use variables con nombres MAYÚSCULAS, o use propiedades para hacer que los valores sean de solo lectura), quiero mencionar que es posible usar metaclases para implementar constantes con nombre . Proporciono una solución muy simple usando metaclases en GitHub que puede ser útil si desea que los valores sean más informativos sobre su tipo / nombre:
Este es Python un poco más avanzado, pero sigue siendo muy fácil de usar y práctico. (El módulo tiene algunas características más, incluidas las constantes de solo lectura, consulte su archivo README).
Hay soluciones similares que flotan en varios repositorios, pero que yo sepa carecen de una de las características fundamentales que esperaría de las constantes (como ser constante o de tipo arbitrario), o tienen características esotéricas agregadas que hacerlos menos aplicables en general. Pero YMMV, agradecería sus comentarios. :-)
fuente
def enum(**enums): return type('Enum', (), enums)
.Numbers = enum(ONE=1, TWO=2, THREE='three')
, según stackoverflow.com/a/1695250/199364 , sección "En versiones anteriores ..."Las propiedades son una forma de crear constantes. Puede hacerlo declarando una propiedad getter, pero ignorando el setter. Por ejemplo:
Puedes echar un vistazo a un artículo que he escrito para encontrar más formas de usar las propiedades de Python.
fuente
Editar: Código de muestra agregado para Python 3
Nota: esta otra respuesta parece que proporciona una implementación mucho más completa, similar a la siguiente (con más funciones).
Primero, crea una metaclase :
Esto evita que se cambien las propiedades estáticas. Luego crea otra clase que use esa metaclase:
O, si estás usando Python 3:
Esto debería evitar que se modifiquen los accesorios de instancia. Para usarlo, herede:
Ahora los accesorios, a los que se accede directamente o mediante una instancia, deben ser constantes:
Aquí hay un ejemplo de lo anterior en acción. Aquí hay otro ejemplo para Python 3.
fuente
Puede usar una tupla con nombre como solución alternativa para crear efectivamente una constante que funcione de la misma manera que una variable final estática en Java (una "constante" de Java). A medida que avanzan las soluciones, es algo elegante. (Un enfoque más elegante sería simplemente mejorar el lenguaje Python --- ¿qué tipo de lenguaje te permite redefinir
math.pi
? - pero estoy divagando).(Mientras escribo esto, me doy cuenta de que otra respuesta a esta pregunta mencionó namedtuple, pero continuaré aquí porque mostraré una sintaxis que se asemeja más a lo que esperarías en Java, ya que no hay necesidad de crear un named escribir como namedtuple te obliga a hacerlo).
Siguiendo su ejemplo, recordará que en Java debemos definir la constante dentro de alguna clase ; porque no mencionaste un nombre de clase, llamémoslo
Foo
. Aquí está la clase de Java:Aquí está el Python equivalente.
El punto clave que quiero agregar aquí es que no necesita un
Foo
tipo separado (una "tupla anónima llamada sería bueno, aunque eso suene como un oxímoron), así que llamamos a nuestra tupla nombrada_Foo
para que con suerte no lo haga escapar a la importación de módulos.El segundo punto aquí es que creamos inmediatamente una instancia de la nametuple, llamándola
Foo
; no es necesario hacer esto en un paso separado (a menos que lo desee). Ahora puedes hacer lo que puedes hacer en Java:Pero no puedes asignarle:
Reconocimiento: pensé que inventé el enfoque de tupla nombrada, pero luego veo que alguien más dio una respuesta similar (aunque menos compacta). Entonces también noté ¿Qué son las "tuplas con nombre" en Python? , que señala que
sys.version_info
ahora es una tupla con nombre, por lo que quizás la biblioteca estándar de Python ya tuvo esta idea mucho antes.Tenga en cuenta que desafortunadamente (esto sigue siendo Python), puede borrar toda la
Foo
tarea por completo:(facepalm)
Pero al menos estamos evitando que
Foo.CONST_NAME
se cambie el valor, y eso es mejor que nada. Buena suerte.fuente
PEP 591 tiene el calificador 'final'. La aplicación se debe al verificador de tipo.
Entonces puedes hacer:
Nota: la
Final
palabra clave solo es aplicable para la versión Python 3.8fuente
Aquí hay una implementación de una clase "Constantes", que crea instancias con atributos de solo lectura (constantes). Por ejemplo, puede usar
Nums.PI
para obtener un valor que se ha inicializado como3.14159
, yNums.PI = 22
genera una excepción.Gracias a FrozenDict de @MikeGraham , que utilicé como punto de partida. Cambiado, por lo que en lugar de
Nums['ONE']
la sintaxis de uso esNums.ONE
.Y gracias a la respuesta de @ Raufio, por la idea de anular __ setattr __.
O para una implementación con más funcionalidad, vea @Hans_meine's named_constants en GitHub
fuente
Nums._d['PI'] = 22
El lenguaje en sí no proporciona ninguna forma de marcar las cosas como no mutables, creo.Una tupla técnicamente califica como una constante, ya que una tupla generará un error si intenta cambiar uno de sus valores. Si desea declarar una tupla con un valor, coloque una coma después de su único valor, así:
Para verificar el valor de esta variable, use algo similar a esto:
Si intenta cambiar este valor, se generará un error.
fuente
Haría una clase que anule el
__setattr__
método de la clase de objeto base y ajuste mis constantes con eso, tenga en cuenta que estoy usando python 2.7:Para envolver una cadena:
Es bastante simple, pero si desea usar sus constantes de la misma manera que lo haría con un objeto no constante (sin usar constObj.value), será un poco más intenso. Es posible que esto pueda causar problemas, por lo que podría ser mejor mantener el
.value
para mostrar y saber que está haciendo operaciones con constantes (aunque quizás no sea la forma más 'pitónica').fuente
def ONE(): return 1
más simple sugerida anteriormente es más fácil de usarONE()
que esta respuestaONE.value
.Desafortunadamente, Python aún no tiene constantes y es una pena. ES6 ya agregó constantes de soporte a JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ) ya que es algo muy útil en cualquier lenguaje de programación. Como se respondió en otras respuestas en la comunidad de Python, use la convención: la variable en mayúsculas del usuario como constantes, pero no protege contra errores arbitrarios en el código. Si lo desea, puede que le resulte útil una solución de un solo archivo como la siguiente (vea las cadenas de documentos sobre cómo usarla).
archivo constantes.py
Si esto no es suficiente, vea el caso de prueba completo.
Ventajas: 1. Acceso a todas las constantes para todo el proyecto 2. Control estricto para valores de constantes
Carece: 1. No es compatible con los tipos personalizados y el tipo 'dict'
Notas:
Probado con Python3.4 y Python3.5 (estoy usando el 'tox' para ello)
Entorno de prueba:
.
fuente
La forma pitónica de declarar "constantes" es básicamente una variable de nivel de módulo:
Y luego escribe tus clases o funciones. Dado que las constantes son casi siempre números enteros, y también son inmutables en Python, tiene muy pocas posibilidades de alterarlo.
A menos, por supuesto, si establece explícitamente
RED = 2
.fuente
RED = 2
" es el beneficio completo (en otros idiomas) de poder declarar un nombre de variable como "constante".static
tener un único almacenamiento para el valor de todas las instancias de una clase? A menos que exista la posibilidad de declarar una variable estática / clase de hecho.Podemos crear un objeto descriptor.
1) Si quisiéramos trabajar con constantes a nivel de instancia, entonces:
2) si quisiéramos crear constantes solo a nivel de clase, podríamos usar una metaclase que sirva como contenedor para nuestras constantes (nuestros objetos descriptores); Todas las clases que descienden heredarán nuestras constantes (nuestros objetos descriptores) sin ningún riesgo que pueda modificarse.
Si creo una subclase de Foo, esta clase heredará la constante sin la posibilidad de modificarla
fuente
Los diccionarios de Python son mutables, por lo que no parecen ser una buena forma de declarar constantes:
fuente
Aquí hay un truco si quieres constantes y no te importan sus valores:
Solo define clases vacías.
p.ej:
fuente
En python, una constante es simplemente una variable con un nombre en mayúsculas, con palabras separadas por el carácter de subrayado,
p.ej
DAYS_IN_WEEK = 7
El valor es mutable, ya que puede cambiarlo. Pero dadas las reglas para que el nombre te diga que es una constante, ¿por qué lo harías? Quiero decir, ¡es tu programa después de todo!
Este es el enfoque adoptado en Python. No hay
private
palabra clave por la misma razón. Prefije el nombre con un guión bajo y sabrá que está destinado a ser privado. El código puede romper la regla ... así como un programador podría eliminar la palabra clave privada de todos modos.Python podría haber agregado una
const
palabra clave ... pero un programador podría eliminar la palabra clave y luego cambiar la constante si así lo desean, pero ¿por qué hacer eso? Si desea romper la regla, puede cambiarla de todos modos. Pero, ¿por qué molestarse en romper la regla si el nombre deja clara la intención?¿Quizás haya alguna prueba unitaria en la que tenga sentido aplicar un cambio de valor? Para ver qué sucede durante una semana de 8 días, aunque en el mundo real no se puede cambiar el número de días de la semana. Si el lenguaje lo detuvo haciendo una excepción si solo hay un caso en el que necesita romper la regla ... entonces tendría que dejar de declararlo como una constante, a pesar de que todavía es una constante en la aplicación, y hay solo este caso de prueba que ve lo que sucede si se cambia.
El nombre en mayúsculas le dice que está destinado a ser una constante. Eso es lo importante. No es un idioma que imponga restricciones en el código que tiene el poder de cambiar de todos modos.
Esa es la filosofía de python.
fuente
No hay una manera perfecta de hacer esto. Según tengo entendido, la mayoría de los programadores simplemente capitalizarán el identificador, por lo que PI = 3.142 puede entenderse fácilmente como una constante.
Por otro lado, si quieres algo que realmente actúe como una constante, no estoy seguro de que lo encuentres. Con todo lo que hagas siempre habrá alguna forma de editar la "constante", por lo que realmente no será una constante. Aquí hay un ejemplo muy simple y sucio:
Parece que hará que el estilo PHP sea constante.
En realidad, todo lo que se necesita para que alguien cambie el valor es esto:
Esto es lo mismo para todas las otras soluciones que encontrará aquí, incluso las inteligentes que hacen una clase y redefinen el método de atributo establecido, siempre habrá una forma de evitarlas. Así es como es Python.
Mi recomendación es evitar todas las molestias y capitalizar sus identificadores. Realmente no sería una constante adecuada, pero de nuevo nada lo haría.
fuente
Hay una forma más limpia de hacer esto con namedtuple:
Ejemplo de uso
Con este enfoque exacto, puede asignar espacios a sus constantes.
fuente
Tal vez la biblioteca pconst te ayudará ( github ).
$ pip install pconst
[Out] Constant value of "APPLE_PRICE" is not editable.
fuente
Puede usar StringVar o IntVar, etc., su constante es const_val
fuente
Puedes hacerlo con
collections.namedtuple
yitertools
:fuente
(Este párrafo estaba destinado a ser un comentario sobre esas respuestas aquí y allá , que mencionó
namedtuple
, pero se está haciendo demasiado largo para que quepa en un comentario, así que aquí va).El enfoque de doble nombre mencionado anteriormente es definitivamente innovador. Sin embargo, en aras de la exhaustividad, al final de la sección NamedTuple de su documentación oficial , se lee:
En otras palabras, la documentación oficial prefiere usar una forma práctica, en lugar de implementar realmente el comportamiento de solo lectura. Supongo que se convierte en otro ejemplo más de Zen de Python :
fuente
Aquí hay una colección de modismos que creé como un intento de mejorar algunas de las respuestas ya disponibles.
Sé que el uso de la constante no es pitónico, ¡y no debes hacerlo en casa!
¡Sin embargo, Python es un lenguaje tan dinámico! Este foro muestra cómo es posible la creación de construcciones que se vean y se sientan como constantes. Esta respuesta tiene como objetivo principal explorar lo que el lenguaje puede expresar.
Por favor, no seas demasiado duro conmigo :-).
Para más detalles escribí un blog de acompañamiento sobre estos modismos .
En esta publicación, llamaré una variable constante a una referencia constante a valores (inmutables o no). Además, digo que una variable tiene un valor congelado cuando hace referencia a un objeto mutable que un código de cliente no puede actualizar su (s) valor (es).
Un espacio de constantes (SpaceConstants)
Este modismo crea lo que parece un espacio de nombres de variables constantes (también conocido como SpaceConstants). Es una modificación de un fragmento de código de Alex Martelli para evitar el uso de objetos de módulo. En particular, esta modificación usa lo que yo llamo una fábrica de clases porque dentro de la función SpaceConstants , se define una clase llamada SpaceConstants y se devuelve una instancia.
Exploré el uso de class factory para implementar un diseño similar basado en políticas en Python en stackoverflow y también en una publicación de blog .
Un espacio de valores congelados (SpaceFrozenValues)
Este siguiente modismo es una modificación de los SpaceConstants en donde los objetos mutables a los que se hace referencia están congelados. Esta implementación explota lo que llamo cierre compartido entre las funciones setattr y getattr . El valor del objeto mutable es copiado y referenciado por el caché variable definido dentro del cierre compartido de la función. Forma lo que yo llamo una copia protegida de cierre de un objeto mutable .
Debe tener cuidado al usar este modismo porque getattr devuelve el valor de caché haciendo una copia profunda. ¡Esta operación podría tener un impacto significativo en el rendimiento de objetos grandes!
Un espacio constante (ConstantSpace)
Este idioma es un espacio de nombres inmutable de variables constantes o ConstantSpace . Es una combinación de la respuesta increíblemente simple de Jon Betts en stackoverflow con una fábrica de clase .
Un espacio congelado (FrozenSpace)
Este idioma es un espacio de nombres inmutable de variables congeladas o FrozenSpace . Se deriva del patrón anterior al hacer que cada variable sea una propiedad protegida al cerrar la clase FrozenSpace generada .
fuente
En Python, las constantes no existen, pero puede indicar que una variable es una constante y no debe cambiarse agregando
CONST_
al inicio del nombre de la variable y declarando que es una constante en un comentario:Alternativamente, puede crear una función que actúe como una constante:
fuente
En mi caso, necesitaba bytearrays inmutables para la implementación de una biblioteca criptográfica que contiene muchos números literales que quería asegurar que fueran constantes.
Esta respuesta funciona, pero el intento de reasignación de elementos bytearray no genera un error.
Las constantes son inmutables, pero la asignación constante de bytearray falla en silencio:
Un enfoque más poderoso, simple y quizás incluso más 'pitónico' implica el uso de objetos de memoria (objetos de memoria intermedia en <= python-2.6).
La asignación de elementos de ConstArray es un
TypeError
:fuente
Escribo una lib de utilidad para python const: kkconst - pypi support str, int, float, datetime
la instancia del campo const mantendrá su comportamiento de tipo base.
Por ejemplo:
más detalles de uso puedes leer la url de pypi : pypi o github
fuente
Puede ajustar una constante en una matriz numpy, marcarla solo de escritura y siempre llamarla por índice cero.
por supuesto, esto solo protege el contenido del numpy, no la variable "CONSTANTE" en sí misma; aún puedes hacer:
y
CONSTANT
cambiaría, sin embargo, eso arrojaría rápidamente un TypeError la primera vezCONSTANT[0]
que luego se llama en el script.aunque ... supongo que si en algún momento lo cambiaste a
ahora ya no tendrías el TypeError. hmmmm ....
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html
fuente