Como crio uma constante no Python?

991

Existe uma maneira de declarar uma constante em Python? Em Java, podemos criar valores constantes desta maneira:

public static final String CONST_NAME = "Name";

Qual é o equivalente da declaração constante de Java acima em Python?

zfranciscus
fonte
6
na verdade, a maneira de criar variáveis somente leitura é possível através da função de propriedade / decorador do python . a resposta de inv é um exemplo de uso personalizado disso. A propriedade é de uso mais geral do que isso, porém, uma boa análise de como funciona está nos atributos e métodos do Python de Shalabh Chaturvedi .
N611x007
20
IMHO, forçar a constância "não é pitônico". No Python 2.7, você pode até escrever True=Falsee depois (2+2==4)==Trueretornar False.
osa
8
Como outras respostas sugerem, não há como ou não é necessário declarar constantes. Mas você pode ler este PEP sobre convenções. por exemplo THIS_IS_A_CONSTANT
Rasika Perera
34
@osa: Você não pode fazer isso em python 3 - SyntaxError: can't assign to keyword. Isso parece uma coisa boa.
precisa saber é o seguinte
3
Surpreendido, isso não foi mencionado até agora, mas Enums parece uma boa maneira de definir constantes enumeradas.
cs95 9/06/19

Respostas:

973

Não, não há. Você não pode declarar uma variável ou valor como constante no Python. Só não mude.

Se você estiver em uma classe, o equivalente seria:

class Foo(object):
    CONST_NAME = "Name"

se não, é apenas

CONST_NAME = "Name"

Mas você pode dar uma olhada no snippet de código Constants in Python de Alex Martelli.


No Python 3.8, há uma typing.Finalanotação de variável que informa aos verificadores de tipo estático (como mypy) que sua variável não deve ser reatribuída. Este é o equivalente mais próximo do Java final. No entanto, na verdade, não impede a reatribuição :

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
fonte
27
Em vez disso, faça o que está em "Constantes no Python", você deve usar a função ou o decorador "property".
Seth Johnson
26
As pessoas perguntam sobre o mesmo recurso no Perl. Existe um módulo de importação chamado "use constant", mas (AFAIK) é apenas um invólucro para criar uma função minúscula que retorna o valor. Eu faço o mesmo em Python. Exemplo:def MY_CONST_VALUE(): return 123
kevinarpe 17/12/12
8
"Não, não há." É verdade, mas com base no trabalho de outras pessoas, eu adicionei uma resposta, bem abaixo, com uma implementação curta e simples de "Constantes" para python 2.7 (que carece de "enum"). Eles são apenas para leitura do tipo enum name.attributee podem conter qualquer valor. A declaração é fácil Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), o uso é direto print 10 + Nums.PI, tente alterar os resultados na exceção Nums.PI = 22=> ValueError (..).
Home
110
Só não mude. você fez o meu dia
Hi-Angel
89
"Apenas não mude" não ajuda em nada. Ele não responde à pergunta e eu sugiro que ela seja removida.
Bartek Banachewicz
354

Não há constpalavras-chave como em outros idiomas, no entanto, é possível criar uma Propriedade que tenha uma "função getter" para ler os dados, mas nenhuma "função setter" para reescrever os dados. Isso essencialmente protege o identificador de ser alterado.

Aqui está uma implementação alternativa usando a propriedade de classe:

Observe que o código está longe de ser fácil para um leitor se perguntando sobre constantes. Veja a explicação abaixo

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

Código Explicação:

  1. Defina uma função constantque pega uma expressão e a usa para construir um "getter" - uma função que retorna apenas o valor da expressão.
  2. A função setter gera um TypeError para que seja somente leitura
  3. Use a constantfunção que acabamos de criar como decoração para definir rapidamente propriedades somente leitura.

E de alguma outra maneira mais antiquada:

(O código é bastante complicado, mais explicações abaixo)

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

Observe que o decorador @apply parece estar obsoleto.

  1. Para definir o identificador FOO, os primeiros definem duas funções (fset, fget - os nomes estão à minha escolha).
  2. Em seguida, use a propertyfunção interna para construir um objeto que possa ser "definido" ou "obter".
  3. Observe que os propertydois primeiros parâmetros da função são nomeados fsete fget.
  4. Use o fato de escolhermos esses nomes para nosso próprio getter & setter e crie um dicionário de palavras-chave usando o ** (asterisco duplo) aplicado a todas as definições locais desse escopo para passar parâmetros para a propertyfunção
inv
fonte
11
Com base na documentação AttributeErrore TypeError, acho que a exceção levantada deve ser um novo erro, que proponho nomeação ConstantErrorou algo parecido, que é uma subclasse de TypeError. A seção na documentação que me faz pensar que: docs.python.org/2/library/exceptions.html
ArtOfWarfare
3
Estou surpreso com este código. Por que a fonte dos métodos FOO () e BAR () tem auto como argumento? Meu IDE sublinha os colchetes no vermelho (erro de "compilação"). Eu cansei de me colocar nele, mas então eu recebo um erro.
precisa saber é o seguinte
10
Indo para esses comprimentos delineia uma clara deficiência na linguagem python. Por que eles não sentiram a necessidade de adicionar isso ao Python 3. Não acredito que ninguém o sugeriu e simplesmente não consigo ver a lógica por trás de algum comitê que fica nah, constantes? não.
Andrew S
8
E sua solução ainda pode ser modificado por um programador python determinado usandoCONST.__dict__['FOO'] = 7
pppery
11
@OscarSmith, acho que melhoraria o design do 'código auto-documentado'. Quando explico no código que algum valor não pode mudar, é mais fácil entender do que ler todo o código-fonte e perceber que algum valor nunca muda. Além disso, bloqueia a possibilidade de alguém alterar um valor que deve ser, bem, constante. Lembre-se: explícito é melhor que implícito.
Gabriel
112

No Python, em vez de a linguagem impor algo, as pessoas usam convenções de nomenclatura, por exemplo, __methodpara métodos privados e _methodmétodos protegidos.

Portanto, da mesma maneira, você pode simplesmente declarar a constante como todos os limites, por exemplo

MY_CONSTANT = "one"

Se você quiser que essa constante nunca mude, poderá conectar-se ao acesso a atributos e fazer truques, mas uma abordagem mais simples é declarar uma função

def MY_CONSTANT():
    return "one"

O único problema está em todo lugar que você terá que fazer MY_CONSTANT (), mas novamente MY_CONSTANT = "one"é a maneira correta em python (geralmente).

Você também pode usar o namedtuple para criar 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
fonte
18
Fazer def MY_CONSTANT(): return "one"não impedirá alguém, posteriormente no código, de fazer MY_CONSTANT = "two"(ou redeclarar a função).
Matthew Schinckel
6
@MatthewSchinckel, trata-se de convenção, mudar também MY_CONSTANT não mudará o uso MY_CONSTANT (), mas gerará erro e, em python, se você quiser, pode mudar qualquer coisa, nenhum truque inteligente pode protegê-lo.
Anurag Uniyal
3
Agradecemos por apresentar a abordagem de nome duplo. Definitivamente inovador. Você também pode encontrar meu "comentário" aqui relevante.
RayLuo 6/09/19
@ MatthewSchinckel, você pode redefinir QUALQUER COISA em python, então isso não é realmente um bom argumento.
cslotty 2/03
@MatthewSchinckel A idéia não é escrever MY_CONSTANT = MY_CONSTANT(), mas usar MY_CONSTANT()como constante. Claro que sim. Mas isso é bom e está muito alinhado com o princípio do python "Somos todos adultos aqui" - ou seja, o desenvolvedor raramente será proibido de decidir substituir uma regra quando tiver boas razões e saber o que está fazendo.
jonathan.scholbach
69

Recentemente, eu encontrei uma atualização muito sucinta disso, que gera automaticamente mensagens de erro significativas e impede o acesso via __dict__:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

Definimos sobre nós mesmos como nos tornar uma instância e, em seguida, usamos slots para garantir que nenhum atributo adicional possa ser adicionado. Isso também remove a __dict__rota de acesso. Obviamente, todo o objeto ainda pode ser redefinido.

Editar - Solução original

Provavelmente estou perdendo um truque aqui, mas isso parece funcionar para mim:

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

Criar a instância permite que o __setattr__método mágico entre e intercepte as tentativas de definir a FOOvariável. Você poderia lançar uma exceção aqui, se quisesse. A instanciação da instância sobre o nome da classe impede o acesso diretamente através da classe.

É uma dor total para um valor, mas você pode anexar muito ao seu CONSTobjeto. Tendo uma classe alta, o nome da classe também parece um pouco grotty, mas acho que é bastante sucinto no geral.

Jon Betts
fonte
11
Essa é a melhor e mais clara resposta, pois possui o menor "mecanismo", mas a maior funcionalidade. Gerar uma exceção é importante ... não é uma opção.
Erik Aronesty
Eu elaborei uma rota mais curta que produz automaticamente erros significativos, mas é muito parecida com o mesmo. Deixei a ideia original aqui para comparação.
Jon Betts
Que pena que você ainda precise desse CONST.prefixo. Também em situações com vários módulos, isso será complicado.
Alfe
1
Eu acho que normalmente você gostaria de agrupar constantes em alguns pacotes relacionados de qualquer maneira nessa situação (em vez de ter um objeto CONST gigante), então provavelmente não é uma coisa tão ruim.
Jon Betts
Por que essa resposta ainda está tão longe ?! A __slots__solução é tão elegante e eficaz. De tudo o que li, isso é o mais próximo possível da criação de constantes no Python. Muito obrigado. E para todos os interessados, aqui está uma explicação brilhante e profunda da __slots__mágica.
JohnGalt
34

Python não tem constantes.

Talvez a alternativa mais fácil seja definir uma função para ela:

def MY_CONSTANT():
    return 42

MY_CONSTANT() agora tem toda a funcionalidade de uma constante (mais algumas chaves irritantes).

Saeed Baig
fonte
1
Eu só queria adicionar essa sugestão, mas felizmente rolei para as respostas de baixa classificação. Espero que seja mais votado e concordo plenamente que ele tem toda a funcionalidade de uma constante e é muito simples e direto. Olhando para a quantidade de código padrão em todas as soluções sofisticadas, acho as chaves relativamente desagradáveis.
yaccob
1
esta é a resposta mais simples, embora deva-se notar que ela possui alguma sobrecarga e não interromperá os idiotas que modificam o valor de retorno. Ela só vai impedir que o código mais abaixo da linha como alterar a origem
MrMesees
@MrMesees modificando o valor de retorno? Você quer dizer editar a fonte? Mas a partir disso, você não está protegido nem mesmo em C ++, onde constantes (como constexpr) são constantes reais.
Ruslan
@Ruslan, o que eu quis dizer foi que, como o python não tem constexpr, ele não interrompe a edição do valor depois que ele é retornado para um contexto externo. Nada foi feito para 42 para impor o estado congelado neste exemplo.
MrMesees
20

Além das duas respostas principais (basta usar variáveis ​​com nomes UPPERCASE ou usar propriedades para tornar os valores somente leitura), quero mencionar que é possível usar metaclasses para implementar constantes nomeadas . Eu forneço uma solução muito simples usando metaclasses no GitHub, que pode ser útil se você quiser que os valores sejam mais informativos sobre seu tipo / nome:

>>> 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 é um pouco mais avançado do Python, mas ainda é muito fácil de usar e prático. (O módulo possui mais alguns recursos, incluindo constantes sendo somente leitura, consulte o arquivo LEIA-ME.)

Existem soluções semelhantes flutuando em vários repositórios, mas, pelo que sei, elas não possuem um dos recursos fundamentais que eu esperaria das constantes (como sendo constante ou de tipo arbitrário) ou possuem recursos esotéricos que torná-los menos aplicáveis ​​em geral. Mas YMMV, eu ficaria grato pelo feedback. :-)

hans_meine
fonte
3
Gosto da sua implementação no GitHub. Eu estava quase pronto para escrever uma classe básica que implementou a funcionalidade de pesquisa inversa, mas vejo que você fez isso e muito mais!
Kerr
Obrigado, @Kerr, é o primeiro feedback que recebi e me fez feliz. :-)
hans_meine 14/11
Impressionante. Eu apenas tentei isso. É bom ter isso como opção. Embora ainda não tenha decidido se me importo o suficiente com o aspecto somente leitura, use isso em vez de simplesmente fazer def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), conforme stackoverflow.com/a/1695250/199364 , seção "Nas versões anteriores ..."
ToolmakerSteve
19

As propriedades são uma maneira de criar constantes. Você pode fazer isso declarando uma propriedade getter, mas ignorando o setter. Por exemplo:

class MyFinalProperty(object):

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

Você pode dar uma olhada em um artigo que escrevi para encontrar mais maneiras de usar as propriedades do Python.

nvd
fonte
Sob solução valiosa. Acabei de implementar isso depois de encontrar esta página (e não esta resposta) e voltei para adicioná-la, se ainda não estiver. Eu queria enfatizar a utilidade desta resposta.
Marc
18

Edit: Adicionado código de exemplo para Python 3

Nota: esta outra resposta parece oferecer uma implementação muito mais completa semelhante à seguinte (com mais recursos).

Primeiro, faça uma metaclasse :

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

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

Isso evita que as propriedades estáticas sejam alteradas. Em seguida, faça outra classe que use essa metaclasse:

class Const(object):
    __metaclass__ = MetaConst

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

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

Ou, se você estiver usando Python 3:

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

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

Isso deve impedir que os objetos da instância sejam alterados. Para usá-lo, herde:

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

Agora, os objetos acessados ​​diretamente ou por meio de uma instância devem ser constantes:

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

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

Aqui está um exemplo acima em ação. Aqui está outro exemplo para o Python 3.

doubleswirve
fonte
10

Você pode usar um nomeado duplo como solução alternativa para criar efetivamente uma constante que funciona da mesma maneira que uma variável final estática em Java (uma "constante" Java). Como soluções alternativas, é meio elegante. (Uma abordagem mais elegante seria simplesmente melhorar a linguagem Python - que tipo de linguagem permite redefinir math.pi? -, mas discordo.)

(Enquanto escrevo isso, percebo outra resposta para essa pergunta mencionada nomeadaple, mas continuarei aqui porque mostrarei uma sintaxe que se aproxima mais do que você esperaria em Java, pois não há necessidade de criar uma nomeada digite como nome nomeado força você a fazer.)

Seguindo o seu exemplo, você se lembrará de que em Java devemos definir a constante dentro de alguma classe ; porque você não mencionou o nome de uma classe, vamos chamá-lo Foo. Aqui está a classe Java:

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

Aqui está o Python equivalente.

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

O ponto principal que quero acrescentar aqui é que você não precisa de um Footipo separado (uma "tupla nomeada anônima" seria legal, mesmo que pareça um oxímoro), por isso nomeamos nosso nome nomeado _Foopara que, esperemos, não escape para a importação de módulos.

O segundo ponto aqui é que criamos imediatamente uma instância do nametuple, chamando-o Foo; não é necessário fazer isso em uma etapa separada (a menos que você queira). Agora você pode fazer o que pode fazer em Java:

>>> Foo.CONST_NAME
'Name'

Mas você não pode atribuir a ele:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Agradecimento: Pensei ter inventado a abordagem do duplo nome, mas depois vejo que alguém deu uma resposta semelhante (embora menos compacta). Também notei o que são "tuplas nomeadas" no Python? , que salienta que sys.version_infoagora é um nome múltiplo, talvez a biblioteca padrão do Python já tenha tido essa ideia muito antes.

Observe que, infelizmente (ainda sendo Python), você pode apagar toda a Fooatribuição:

>>> Foo = 'bar'

(facepalm)

Mas pelo menos estamos impedindo que o Foo.CONST_NAMEvalor seja alterado, e isso é melhor que nada. Boa sorte.

Garret Wilson
fonte
Agradecemos por apresentar a abordagem de nome duplo. Definitivamente inovador. Você também pode encontrar meu "comentário" aqui relevante.
RayLuo 6/09/18
10

O PEP 591 possui o qualificador 'final'. A aplicação é do verificador de tipos.

Então você pode fazer:

MY_CONSTANT: Final = 12407

Nota: a Final palavra - chave é aplicável apenas à versão Python 3.8

Mike Amy
fonte
9

Aqui está uma implementação de uma classe "Constants", que cria instâncias com atributos somente leitura (constantes). Por exemplo, pode usar Nums.PIpara obter um valor que foi inicializado como 3.14159e Nums.PI = 22gera uma exceção.

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

Graças ao FrozenDict de @MikeGraham , que usei como ponto de partida. Alterado, portanto, em vez da Nums['ONE']sintaxe de uso Nums.ONE.

E, graças à resposta de @ Raufio, pela ideia de substituir __ setattr __.

Ou para uma implementação com mais funcionalidade, consulte named_constants do @Hans_meine no GitHub

Fabricante de ferramentas
fonte
2
Python é uma linguagem de adultos que consentem. Não há proteção contra algo assim. Nums._d['PI'] = 22 A linguagem em si não fornece nenhuma maneira de marcar as coisas como não mutáveis, acredito.
precisa
8

Uma tupla tecnicamente é qualificada como uma constante, pois ela gera um erro se você tentar alterar um de seus valores. Se você deseja declarar uma tupla com um valor, coloque uma vírgula após seu único valor, desta forma:

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

Para verificar o valor dessa variável, use algo semelhante a este:

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

Se você tentar alterar esse valor, um erro será gerado.

tobahhh
fonte
7

Eu faria uma classe que substituísse o __setattr__método da classe de objeto base e envolvesse minhas constantes com isso, observe que estou usando o 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 quebrar uma sequência:

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

É bem simples, mas se você quiser usar suas constantes da mesma forma que faria com um objeto não constante (sem usar constObj.value), será um pouco mais intenso. É possível que isso possa causar problemas; portanto, é melhor manter o .valueprograma para mostrar e saber que você está fazendo operações com constantes (embora talvez não seja o modo mais 'pitônico').

Raufio
fonte
+1 para uma abordagem interessante. Embora não seja tão limpo quanto as respostas que já foram fornecidas. E mesmo a solução def ONE(): return 1mais simples sugerida anteriormente é mais fácil de usar do ONE()que esta resposta ONE.value.
Home
7

Infelizmente, o Python ainda não tem constantes e é uma pena. O ES6 já adicionou constantes de suporte ao JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ), pois é algo muito útil em qualquer linguagem de programação. Conforme respondido em outras respostas na comunidade Python, use a variável convention - user uppercase como constantes, mas ela não protege contra erros arbitrários no código. Se desejar, pode ser considerada uma solução de arquivo único útil a seguir (consulte as instruções de como usá-la).

arquivo constants.py

import collections


__all__ = ('const', )


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

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

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

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

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

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

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

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

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

        return self.__dict__


const = Constant()


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

Se isso não for suficiente, consulte o caso de teste 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"})

Vantagens: 1. Acesso a todas as constantes para todo o projeto 2. Controle rigoroso dos valores das constantes

Falta: 1. Não suporta tipos personalizados e o tipo 'dict'

Notas:

  1. Testado com Python3.4 e Python3.5 (eu uso o 'tox' para isso)

  2. Ambiente de teste:

.

$ 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
fonte
Você pode melhorar isso ligeiramente convertendo dicionários automaticamente para tuplas nomeadas
Peter Schorn
6

A maneira Pythonic de declarar "constantes" é basicamente uma variável no nível do módulo:

RED = 1
GREEN = 2
BLUE = 3

E então escreva suas classes ou funções. Como as constantes são quase sempre inteiras e também são imutáveis ​​no Python, você tem muito pouca chance de alterá-la.

A menos, é claro, se você definir explicitamente RED = 2.

Xavier Ho
fonte
21
Sim, mas bloquear a capacidade de "definir explicitamente RED = 2" é o benefício inteiro (em outros idiomas) de poder declarar um nome de variável como "constante"!
Página
6
Você se beneficiaria com isso? A coisa mais útil sobre const é geralmente otimizações de compilador que não são realmente uma coisa no Python. Quer que algo seja constante? Só não mude. Se você está preocupado com alguém que está mudando, você pode simplesmente colocá-lo fora do escopo deles ou apenas perceber que, se alguém está mudando, isso é problema deles e eles precisam lidar com isso, não você.
Kevin
@ Kevin: " Você se beneficiaria com ... ", o benefício de staticter um único armazenamento para o valor de todas as instâncias de uma classe? A menos que haja a possibilidade de declarar uma variável estática / classe de fato.
mins
8
O problema principal é que alguns podem vê-lo como um valor que é uma fonte de verdade, incapaz de ser alterado, e usá-lo como fonte de verdade em todo o código, em vez de introduzir valores mágicos (que eu vejo muito em Python) - e outros podem vê-lo como algo que eles podem mudar à vontade. Quando alguém altera uma variável global e você não pode dizer onde ela foi alterada, e o aplicativo trava porque RED = "azul" em vez de "vermelho", você está apresentando um problema totalmente desnecessário que já foi resolvido de maneira simples e simples. é universalmente entendido.
Dagrooms
5

Podemos criar um objeto descritor.

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) Se quisermos trabalhar com constantes no nível da instância, então:

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) se desejássemos criar constantes apenas no nível de classe, poderíamos usar uma metaclasse que serve como um contêiner para nossas constantes (nossos objetos descritores); todas as classes descendentes herdarão nossas constantes (nossos objetos descritores) sem nenhum risco que possa ser modificado.

# 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

Se eu criar uma subclasse de Foo, essa classe herdará a constante sem a possibilidade de modificá-la

class Bar(Foo): pass

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

Os dicionários Python são mutáveis, portanto, não parecem uma boa maneira de declarar constantes:

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

Aqui está um truque se você deseja constantes e não se importa com seus valores:

Apenas defina classes vazias.

por exemplo:

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

Em python, uma constante é simplesmente uma variável com um nome em todas as maiúsculas, com palavras separadas pelo caractere sublinhado,

por exemplo

DAYS_IN_WEEK = 7

O valor é mutável, pois você pode alterá-lo. Mas, dadas as regras do nome, é uma constante, por que você o faria? Quero dizer, é o seu programa, afinal!

Essa é a abordagem adotada no python. Não há privatepalavra-chave pelo mesmo motivo. Prefixe o nome com um sublinhado e você sabe que ele deve ser privado. O código pode violar a regra .... assim como um programador pode remover a palavra-chave privada de qualquer maneira.

O Python poderia ter adicionado uma constpalavra - chave ... mas um programador pode remover a palavra-chave e alterar a constante, se quiser, mas por que fazer isso? Se você quiser violar a regra, poderá alterá-la de qualquer maneira. Mas por que se preocupar em quebrar a regra se o nome deixa clara a intenção?

Talvez haja algum teste de unidade em que faça sentido aplicar uma alteração no valor? Para ver o que acontece durante uma semana de 8 dias, embora no mundo real o número de dias da semana não possa ser alterado. Se o idioma o impedisse de fazer uma exceção, se houver apenas um caso, você precisará violar a regra ... você teria que parar de declará-lo como uma constante, mesmo que ainda seja uma constante no aplicativo, e exista apenas este caso de teste que vê o que acontece se for alterado.

O nome todo em maiúsculas informa que ele pretende ser uma constante. Isso é o que é importante. Não é uma linguagem que força restrições no código que você pode alterar de qualquer maneira.

Essa é a filosofia do python.

innov8
fonte
4

Não existe uma maneira perfeita de fazer isso. Pelo que entendi, a maioria dos programadores apenas coloca o identificador em maiúscula, portanto PI = 3.142 pode ser facilmente entendido como uma constante.

Por outro lado, se você quer algo que realmente age como uma constante, não tenho certeza de que o encontrará. Com qualquer coisa que você faça, sempre haverá uma maneira de editar a "constante", para que não seja realmente uma constante. Aqui está um exemplo muito simples e sujo:

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 isso fará uma constante no estilo PHP.

Na realidade, basta que alguém mude o valor:

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

É o mesmo para todas as outras soluções que você encontrará aqui - mesmo as mais inteligentes que formam uma classe e redefinem o método set attribute - sempre haverá uma maneira de contorná-las. É assim que o Python é.

Minha recomendação é evitar todo o aborrecimento e apenas colocar seus identificadores em maiúscula. Não seria realmente uma constante adequada, mas, novamente, nada seria.

John
fonte
4

Existe uma maneira mais limpa de fazer isso com o nomeduplo:

from collections import namedtuple


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

Exemplo de uso

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

Com essa abordagem exata, você pode colocar no namespace suas constantes.

Juan Ignacio Sánchez
fonte
Para quem está lendo isso, lembre-se de que, se você definir um objeto mutável como uma dessas constantes, qualquer um poderá alterar seu valor interno. por exemplo, vamos bar = [1, 2, 3], então você pode fazer o seguinte: CONSTS.bar [1] = 'a' e não será rejeitado. Portanto, tenha cuidado com isso.
Juan Ignacio Sánchez
Em vez desse método hacky, que eu criei apenas por diversão, recomendo usar o decorador de propriedades do Python.
Juan Ignacio Sánchez
4

Talvez a biblioteca pconst o ajude ( 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
fonte
3

Você pode usar StringVar ou IntVar, etc, sua constante é const_val

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

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

Você pode fazer isso com collections.namedtuplee 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
fonte
2

(Este parágrafo foi feito para ser um comentário sobre as respostas aqui e ali , que mencionamos namedtuple, mas está demorando muito para caber em um comentário, então, aqui vai.)

A abordagem de nomeação dupla mencionada acima é definitivamente inovadora. Para fins de completude, porém, no final da seção NamedTuple de sua documentação oficial , ele lê:

constantes enumeradas podem ser implementadas com tuplas nomeadas, mas é mais simples e mais eficiente usar uma declaração de classe simples:

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

Em outras palavras, a documentação oficial prefere usar uma maneira prática, em vez de realmente implementar o comportamento somente leitura. Eu acho que se torna mais um exemplo do Zen of Python :

Simples é melhor que complexo.

praticidade supera a pureza.

RayLuo
fonte
2

Aqui está uma coleção de idiomas que criei como uma tentativa de melhorar algumas das respostas já disponíveis.

Eu sei que o uso de constante não é pitônico, e você não deve fazer isso em casa!

No entanto, Python é uma linguagem tão dinâmica! Este fórum mostra como é possível a criação de construções que pareçam constantes. Esta resposta tem como objetivo principal explorar o que pode ser expresso pelo idioma.

Por favor, não seja muito duro comigo :-).

Para mais detalhes, escrevi um blog de acompanhamento sobre esses idiomas .

Neste post, chamarei uma variável constante para uma referência constante a valores (imutáveis ​​ou não). Além disso, digo que uma variável tem um valor congelado quando faz referência a um objeto mutável que um código de cliente não pode atualizar seu (s) valor (es).

Um espaço de constantes (SpaceConstants)

Esse idioma cria o que parece ser um espaço para nome de variáveis ​​constantes (também conhecido como SpaceConstants). É uma modificação de um trecho de código por Alex Martelli para evitar o uso de objetos de módulo. Em particular, essa modificação usa o que eu chamo de fábrica de classes porque, dentro da função SpaceConstants , uma classe chamada SpaceConstants é definida e uma instância dela é retornada.

Explorei o uso do class factory para implementar um design baseado em políticas em Python no stackoverflow e também em um 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"

Um espaço de valores congelados (SpaceFrozenValues)

Este próximo idioma é uma modificação dos SpaceConstants em que objetos mutáveis ​​referenciados são congelados. Esta implementação explora o que eu chamo de fecho partilhado entre SetAttr e GetAttr funções. O valor do objeto mutável é copiado e referenciado pelo cache variável definido dentro do fechamento compartilhado da função. Ele forma o que eu chamo de cópia protegida de fechamento de um objeto mutável .

Você deve ter cuidado ao usar esse idioma, porque o getattr retorna o valor do cache fazendo uma cópia detalhada . Esta operação pode ter um impacto significativo no desempenho 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"

Um espaço constante (ConstantSpace)

Esse idioma é um espaço para nome imutável de variáveis ​​constantes ou ConstantSpace . É uma combinação da resposta incrivelmente simples de Jon Betts no stackoverflow com uma fábrica de classes .

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

Um espaço congelado (FrozenSpace)

Esse idioma é um espaço para nome imutável de variáveis ​​congeladas ou FrozenSpace . É derivado do padrão anterior, tornando cada variável uma propriedade protegida pelo fechamento da classe FrozenSpace gerada .

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
fonte
2

No Python, as constantes não existem, mas você pode indicar que uma variável é uma constante e não deve ser alterada, adicionando CONST_ao início do nome da variável e afirmando que é uma constante em um comentário:

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.

Como alternativa, você pode criar uma função que atua como uma constante:

def CONST_daysInWeek():
    return 7;

fonte
1

No meu caso, eu precisava de marcadores de distância imutáveis ​​para a implementação de uma biblioteca de criptografia contendo muitos números literais que eu queria garantir que fossem constantes.

Esta resposta funciona, mas a tentativa de reatribuição de elementos de bytearray não gera um erro.

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

As constantes são imutáveis, mas a atribuição constante de um bytearray falha silenciosamente:

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

Uma abordagem mais poderosa, simples e talvez até mais 'pitônica' envolve o uso de objetos memoryview (objetos de buffer em <= 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)

A atribuição de itens do ConstArray é 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
fonte
1

Eu escrevo uma lib util para python const: kkconst - pypi support str, int, float, datetime

a instância do campo const manterá seu comportamento do tipo base.

Por exemplo:

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

mais detalhes sobre o uso, você pode ler o pypi url: pypi ou github

kaka_ace
fonte
1

Você pode agrupar uma constante em uma matriz numpy, sinalizá-la apenas para gravação e sempre chamá-la pelo índice zero.

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

é claro que isso apenas protege o conteúdo do numpy, não a variável "CONSTANT"; você ainda pode fazer:

CONSTANT = 'foo'

e CONSTANTmudaria, no entanto, isso lançaria rapidamente um TypeError na primeira vez em que CONSTANT[0]for chamado posteriormente no script.

embora ... suponho que, em algum momento, você tenha alterado para

CONSTANT = [1,2,3]

agora você não receberá mais o TypeError. hmmmm ....

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

presença
fonte