¿Cómo creo una constante en Python?

992

¿Hay alguna manera de declarar una constante en Python? En Java podemos crear valores constantes de esta manera:

public static final String CONST_NAME = "Name";

¿Cuál es el equivalente de la declaración constante de Java anterior en Python?

zfranciscus
fuente
66
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:

class Foo(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 import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
Felix Kling
fuente
27
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

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

Explicación del código:

  1. Defina una función constantque tome una expresión y la use para construir un "captador", una función que únicamente devuelve el valor de la expresión.
  2. La función setter genera un TypeError, por lo que es de solo lectura
  3. Use la constantfunció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)

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

Tenga en cuenta que el decorador @apply parece estar en desuso.

  1. Para definir el identificador FOO, los primeros definen dos funciones (fset, fget: los nombres son a mi elección).
  2. Luego use la propertyfunción incorporada para construir un objeto que pueda "establecerse" o "obtenerse".
  3. Tenga en cuenta que los propertydos primeros parámetros de la función se denominan fsety fget.
  4. 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
inv
fuente
11
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.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Anurag Uniyal
fuente
18
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í:

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

Jon Betts
fuente
11
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():
    return 42

MY_CONSTANT() ahora tiene toda la funcionalidad de una constante (además de algunos aparatos molestos).

Saeed Baig
fuente
1
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 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

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. :-)

hans_meine
fuente
3
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:

class MyFinalProperty(object):

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

Puedes echar un vistazo a un artículo que he escrito para encontrar más formas de usar las propiedades de Python.

nvd
fuente
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).

Primero, crea una metaclase :

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

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

Esto evita que se cambien las propiedades estáticas. Luego crea otra clase que use esa metaclase:

class Const(object):
    __metaclass__ = MetaConst

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

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

O, si estás usando Python 3:

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

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

Esto debería evitar que se modifiquen los accesorios de instancia. Para usarlo, herede:

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

Ahora los accesorios, a los que se accede directamente o mediante una instancia, deben ser constantes:

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

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

Aquí hay un ejemplo de lo anterior en acción. Aquí hay otro ejemplo para Python 3.

Dobleswirve
fuente
10

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 class Foo {
  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.

Garret Wilson
fuente
Gracias por mencionar el enfoque de tupla nombrada. Definitivamente innovador. También puede encontrar mi "comentario" aquí relevante.
RayLuo
10

PEP 591 tiene el calificador 'final'. La aplicación se debe al verificador de tipo.

Entonces puedes hacer:

MY_CONSTANT: Final = 12407

Nota: la Final palabra clave solo es aplicable para la versión Python 3.8

Mike Amy
fuente
9

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 ----------
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 (__name__ == "__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

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

O para una implementación con más funcionalidad, vea @Hans_meine's named_constants en GitHub

ToolmakerSteve
fuente
2
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.

tobahhh
fuente
7

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):
        raise ValueError("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').

Raufio
fuente
+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', )


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 __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 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"})

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:

  1. Probado con Python3.4 y Python3.5 (estoy usando el 'tox' para ello)

  2. Entorno de prueba:

.

$ 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
PADYMKO
fuente
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.

Xavier Ho
fuente
21
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.

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 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 =100

Traceback (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 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 creo una subclase de Foo, esta clase heredará la constante sin la posibilidad de modificarla

class Bar(Foo): pass

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

Los diccionarios de Python son mutables, por lo que no parecen ser una buena forma de declarar constantes:

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

Aquí hay un truco si quieres constantes y no te importan sus valores:

Solo define clases vacías.

p.ej:

class RED: 
    pass
class BLUE: 
    pass
Lym Zoy
fuente
4

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.

Esa es la filosofía de python.

innov8
fuente
4

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:

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"))

Parece que hará que el estilo PHP sea constante.

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.

Juan
fuente
4

Hay una forma más limpia de hacer esto con namedtuple:

from collections import namedtuple


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

Ejemplo de uso

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

Con este enfoque exacto, puede asignar espacios a sus constantes.

Juan Ignacio Sánchez
fuente
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.

sim
fuente
3

Puede usar StringVar o IntVar, etc., su constante es const_val

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

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

Puedes hacerlo con collections.namedtupley 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
Jaunty Sankey
fuente
2

(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:

class Status:
    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 :

Simple es mejor que complejo.

la practicidad vence a la pureza.

RayLuo
fuente
2

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 .

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

sc = SpaceConstants()

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

Un 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

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

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

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

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

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

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

Un 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

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'"
Victor Bazterra
fuente
2

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:

def CONST_daysInWeek():
    return 7;

fuente
1

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`'''
        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

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
    raise ConstError
__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:
        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)

La asignación de elementos de ConstArray es un 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
jxqz
fuente
1

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,
)

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

más detalles de uso puedes leer la url de pypi : pypi o github

kaka_ace
fuente
1

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

CONSTANT = [1,2,3]

ahora ya no tendrías el TypeError. hmmmm ....

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

presencia lite
fuente