Pregunta ¿Cómo creo una constante en Python?


¿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 de la constante Java anterior en Python?


714
2018-04-21 12:20


origen


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:

class Foo(object):
    CONST_NAME = "Name"

si no, es solo

CONST_NAME = "Name"

Pero es posible que desee echarle un vistazo al fragmento de código Constantes en Python por Alex Martelli.


725
2018-04-21 12:21



No hay const palabra clave como en otros idiomas, sin embargo, es posible crear una propiedad que tiene una "función getter" para leer los datos, pero no hay "función de establecimiento" para volver a escribir los datos. Esto esencialmente protege el identificador de ser cambiado.

Aquí hay una implementación alternativa que usa propiedad de clase:

Tenga en cuenta que el código no es nada fácil para un lector que se pregunta sobre las constantes. Ver explicación a continuación

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. Definir una función constant eso toma una expresión y la usa 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. Utilizar el constant función que acabamos de crear como decoración para definir rápidamente las propiedades de solo lectura.

Y de alguna otra manera 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 están a mi elección).
  2. Luego usa el built-in property función para construir un objeto que se puede "establecer" o "obtener".
  3. Note que el property los dos primeros parámetros de la función se nombran fset y fget.
  4. Use el hecho de que elegimos estos mismos nombres para nuestro propio getter & setter y creamos un diccionario de palabras clave usando el ** (asterisco doble) aplicado a todas las definiciones locales de ese alcance para pasar parámetros al property función

294
2018-04-22 03:39



En Python, en lugar de que el lenguaje imponga algo, las personas usan convenciones de nomenclatura, p. Ej. __method para métodos privados y uso _method para métodos protegidos

De la misma manera, simplemente puede declarar la constante como mayúsculas, por ej.

MY_CONSTANT = "one"

Si desea que esta constante nunca cambie, puede conectar el acceso a los atributos y hacer trucos, pero un enfoque más simple es declarar una función

def MY_CONSTANT():
    return "one"

El único problema es que en todas partes tendrá que hacer MY_CONSTANT (), pero nuevamente MY_CONSTANT = "one" es la forma correcta en python (generalmente).

También puedes 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

82
2018-04-21 13:06



Probablemente me esté 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

Crear la instancia permite la magia __setattr__ método para patear e interceptar los intentos de establecer el FOO variable. Podrías lanzar una excepción aquí si quisieras. Instanciar la instancia sobre el nombre de la clase impide el acceso directamente a través de la clase.

Es un dolor total por un valor, pero podría conectar lotes a su CONST objeto. Tener una clase superior, el nombre de clase también parece un poco sucio, pero creo que es bastante breve en general.


31
2018-04-24 16:00



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

Esto es un poco más avanzado de Python, pero sigue siendo muy fácil de usar y práctico. (El módulo tiene algunas características más, incluidas las constantes que son de solo lectura, vea su archivo README).

Existen soluciones similares en varios repositorios, pero a mi leal saber y entender, carecen de una de las características fundamentales que esperaría de las constantes (como ser constante o ser de tipo arbitrario), o tienen características esotéricas añadidas hacerlos menos aplicables en general. Pero YMMV, estaría agradecido por los comentarios. :-)


15
2017-10-10 21:25



Como probablemente ya sepa, Python no tiene constantes :(

Quizás la alternativa más fácil es definir una función para él. P.ej.

def MY_CONSTANT():
    return 42

MY_CONSTANT() ahora tiene toda la funcionalidad de una constante (más algunas llaves molestas).


13
2017-09-08 22:01



Editar: Código de ejemplo 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 características).

Primero, haz una metaclass:

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 bien, si está utilizando 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 los accesorios de instancia se modifiquen. Para usarlo, hereda:

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

Ahora los accesorios, accedidos directamente o a través de 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í está un ejemplo de arriba en acción. Aquí está otro ejemplo para Python 3.


12
2017-10-03 00:28



Las propiedades son una forma de crear constantes. Puedes hacerlo declarando una propiedad getter, pero ignorando al 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.


6
2017-07-12 22:51



Aquí hay una implementación de una clase "Constantes", que crea instancias con atributos de solo lectura (constantes). P.ej. puedo usar Nums.PI para obtener un valor que se ha inicializado como 3.14159y Nums.PI = 22 plantea 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, entonces en lugar de Nums['ONE'] la sintaxis de uso es Nums.ONE.

Y gracias a la respuesta de @ Raufio, para la idea de anular __ setattr __.

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


5
2017-12-11 00:19



Haría una clase que anula el __setattr__ método de la clase de objeto base y ajustar 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 quieres usar tus constantes de la misma forma que lo harías con un objeto no constante (sin usar constObj.value), será un poco más intensivo. Es posible que esto pueda causar problemas, por lo que podría ser mejor mantener el .value para mostrar y saber que estás haciendo operaciones con constantes (aunque quizás no sea la forma más "pitónica").


4
2018-06-10 00:09