Jak utworzyć stałą w Pythonie?


988

Czy istnieje sposób zadeklarowania stałej w Pythonie? W Javie możemy tworzyć stałe wartości w następujący sposób:

public static final String CONST_NAME = "Name";

Jaki jest odpowiednik powyższej stałej deklaracji Java w Pythonie?


6
w rzeczywistości sposób tworzenia zmiennych tylko do odczytu jest możliwy za pomocą funkcji właściwości / dekoratora Pythona . odpowiedź od inw jest przykładem użycia niestandardowego tego. właściwość jest jednak bardziej ogólna niż ogólna, jednak dobrą analizą jej działania są atrybuty i metody Pythona Shalabh Chaturvedi .
n611x007,

20
IMHO, wymuszanie stałości jest „nie pytoniczne”. W Pythonie 2.7 możesz nawet pisać True=False, a potem (2+2==4)==Truewraca False.
osa

8
Jak sugerują inne odpowiedzi, nie ma możliwości ani nie trzeba deklarować stałych. Ale możesz przeczytać ten PEP o konwencjach. np. THIS_IS_A_CONSTANT
Rasika Perera

34
@osa: Nie możesz tego zrobić w Pythonie 3 - SyntaxError: can't assign to keyword. To wydaje się być dobrą rzeczą.
naught101

3
Zaskoczony, jak dotąd nie wspomniano o tym, ale Enums wydaje się dobrym sposobem na zdefiniowanie wyliczonych stałych.
cs95

Odpowiedzi:


973

Nie, nie ma. Nie można zadeklarować zmiennej lub wartości jako stałej w Pythonie. Tylko tego nie zmieniaj.

Jeśli jesteś w klasie, odpowiednikiem byłoby:

class Foo(object):
    CONST_NAME = "Name"

jeśli nie, to po prostu

CONST_NAME = "Name"

Ale możesz rzucić okiem na fragment kodu Stałe w Pythonie autorstwa Alexa Martellego.


Począwszy od wersji Python 3.8, dostępna jest typing.Finaladnotacja zmiennej, która powie statycznym kontrolerom typu (takim jak mypy), że zmienna nie powinna zostać przypisana ponownie. Jest to najbliższy odpowiednik Javy final. Jednak w rzeczywistości nie zapobiega to zmianie przypisania :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

27
Zamiast tego rób to, co jest w „Stałych w Pythonie”, powinieneś użyć funkcji „właściwość” lub dekoratora.
Seth Johnson

26
Ludzie pytają o tę samą funkcję w Perlu. Istnieje moduł importu o nazwie „użyj stałej”, ale (AFAIK) to tylko opakowanie do stworzenia małej funkcji, która zwraca wartość. Robię to samo w Pythonie. Przykład:def MY_CONST_VALUE(): return 123
kevinarpe

8
"Nie, nie ma." To prawda, ale bazując na pracach innych ludzi, dodałem odpowiedź, poniżej, z krótką i prostą implementacją „Stałych” dla Pythona 2.7 (którym brakuje „enum”). Są one jak wyliczenia tylko do odczytu name.attributei mogą zawierać dowolną wartość. Deklaracja jest łatwa Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), użycie jest proste print 10 + Nums.PI, próba zmiany wyników w wyjątku Nums.PI = 22=> ValueError (..).
ToolmakerSteve

108
Tylko tego nie zmieniaj. zrobiłeś mój dzień
Hi-Angel

89
„Po prostu nie zmieniaj” wcale nie jest pomocne. Nie odpowiada na pytanie i sugerowałbym, aby go usunąć.
Bartek Banachewicz

354

Nie ma constsłowa kluczowego, jak w innych językach, jednak możliwe jest utworzenie właściwości, która ma „funkcję pobierającą” do odczytu danych, ale nie ma „funkcji ustawiającej” do ponownego zapisywania danych. Zasadniczo chroni to identyfikator przed zmianą.

Oto alternatywna implementacja wykorzystująca właściwość klasy:

Zauważ, że kod nie jest łatwy dla czytelnika zastanawiającego się nad stałymi. Zobacz wyjaśnienie poniżej

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

Objaśnienie kodu:

  1. Zdefiniuj funkcję, constantktóra pobiera wyrażenie i używa go do skonstruowania „gettera” - funkcji, która wyłącznie zwraca wartość wyrażenia.
  2. Funkcja ustawiająca wywołuje błąd typu Błąd, więc jest on tylko do odczytu
  3. Skorzystaj z constantfunkcji, którą właśnie stworzyliśmy, jako dekoracji, aby szybko zdefiniować właściwości tylko do odczytu.

I w inny, staromodny sposób:

(Kod jest dość trudny, więcej wyjaśnień poniżej)

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

Pamiętaj, że dekorator @apply wydaje się przestarzały.

  1. Aby zdefiniować identyfikator FOO, jodły definiują dwie funkcje (fset, fget - nazwy są do wyboru).
  2. Następnie użyj wbudowanej propertyfunkcji, aby skonstruować obiekt, który można „ustawić” lub „uzyskać”.
  3. Uwaga property: pierwsze dwa parametry funkcji są nazwane fseti fget.
  4. Skorzystaj z faktu, że wybraliśmy te same nazwy dla własnego gettera i settera i utwórz słownik słów kluczowych, używając ** (podwójnej gwiazdki) zastosowanej do wszystkich lokalnych definicji tego zakresu, aby przekazać parametry do propertyfunkcji

11
W oparciu o dokumentację AttributeErrori TypeErrormyślę, że podniesiony wyjątek powinien być nowym błędem, który proponuję nazwać ConstantErrorlub czymś takim, co jest podklasą TypeError. Sekcja w dokumentach, która każe mi myśleć, że: docs.python.org/2/library/exceptions.html
ArtOfWarfare

3
Jestem zaskoczony tym kodem. Dlaczego czcionki metod FOO () i BAR () mają argument za sobą? Moje IDE podkreśla nawiasy na czerwono (błąd „kompilacji”). Zmęczyłem się włożeniem w to, ale wtedy pojawia się błąd.
user3770060 19.04.2016

10
Przejście do tych długości powoduje wyraźny niedobór języka Python. Dlaczego nie czuli potrzeby dodawania tego do Pythona 3. Nie mogę uwierzyć, że nikt tego nie zasugerował i po prostu nie widzę logiki stojącej za jakimś komitetem, no nie, stałe? nie.
Andrew S

8
A twoje rozwiązanie może być nadal modyfikowane przez określonego programistę python, używającCONST.__dict__['FOO'] = 7
pppery

11
@OscarSmith, myślę, że poprawiłoby to projekt „samodokumentowanego kodu”. Kiedy wyjaśniam w kodzie, że pewna wartość nie może się zmienić, łatwiej jest to zrozumieć niż czytając cały kod źródłowy i zdając sobie sprawę, że jakaś wartość nigdy się nie zmienia. Ponadto blokuje możliwość zmiany wartości, która powinna być, no cóż, stała. Pamiętaj: jawne jest lepsze niż niejawne.
Gabriel

112

W Pythonie zamiast języka wymuszającego coś, ludzie używają konwencji nazewnictwa np. __methodDla metod prywatnych i _methodchronionych.

W ten sam sposób możesz po prostu zadeklarować stałą jak wszystkie wielkie litery np

MY_CONSTANT = "one"

Jeśli chcesz, aby ta stała nigdy się nie zmieniała, możesz podłączyć się do dostępu do atrybutów i wykonywać sztuczki, ale prostszym podejściem jest zadeklarowanie funkcji

def MY_CONSTANT():
    return "one"

Jedynym problemem jest to, że wszędzie będziesz musiał zrobić MY_CONSTANT (), ale znowu MY_CONSTANT = "one"jest to poprawny sposób w pythonie (zwykle).

Możesz także użyć namedtuple do tworzenia stałych:

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

17
Wykonanie tej czynności def MY_CONSTANT(): return "one"nie powstrzyma kogoś, później w kodzie, zrobienia MY_CONSTANT = "two"(lub ponownego opublikowania funkcji).
Matthew Schinckel

6
@MatthewSchinckel chodzi o konwencję, również zmiana MY_CONSTANT nie zmieni użycia MY_CONSTANT (), ale wyrzuci błąd, aw pythonie, jeśli chcesz, możesz coś zmienić, żadna sprytna sztuczka nie może cię ochronić.
Anurag Uniyal

3
Dzięki za wprowadzenie metody namedtuple. Zdecydowanie innowacyjny. Może również okazać się, że mój „komentarz” jest istotny.
RayLuo

@MatthewSchinckel możesz na nowo zdefiniować WSZYSTKO w pythonie, więc to naprawdę nie jest dobry punkt.
cslotty

@MatthewSchinckel Nie chodzi o pisanie MY_CONSTANT = MY_CONSTANT(), ale o używanie MY_CONSTANT()jako stałej. Oczywiście to. Ale to jest w porządku i jest w dużej mierze zgodne z zasadą pytona „Wszyscy jesteśmy tutaj dorośli” - tj. Deweloperowi rzadko będzie zabronione podejmowanie decyzji o zastąpieniu reguły, jeśli mają dobre powody i wiedzą, co robią.
jonathan.scholbach

69

Niedawno znalazłem bardzo zwięzłą aktualizację tego, która automatycznie podnosi znaczące komunikaty o błędach i uniemożliwia dostęp przez __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'

Definiujemy siebie jako instancję, a następnie używamy gniazd, aby upewnić się, że nie można dodać żadnych dodatkowych atrybutów. Spowoduje to również usunięcie __dict__trasy dostępu. Oczywiście cały obiekt można jeszcze przedefiniować.

Edycja - oryginalne rozwiązanie

Prawdopodobnie brakuje mi tutaj podstępu, ale wydaje mi się, że to działa:

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

Utworzenie instancji pozwala magicznej __setattr__metodzie na rozpoczęcie i przechwycenie prób ustawienia FOOzmiennej. Możesz rzucić tutaj wyjątek, jeśli chcesz. Tworzenie instancji nad nazwą klasy uniemożliwia dostęp bezpośrednio przez klasę.

Jest to całkowity ból dla jednej wartości, ale możesz dołączyć wiele do swojego CONSTobiektu. Mając wyższą klasę, nazwa klasy również wydaje się nieco ponura, ale myślę, że jest ogólnie zwięzła.


11
To najlepsza i najbardziej klarowna odpowiedź, ponieważ ma najmniejszy „mechanizm”, ale największą funkcjonalność. Zgłaszanie wyjątku jest ważne, ale ... nie ma opcji.
Erik Aronesty,

Opracowałem krótszą trasę, która automatycznie powoduje znaczące błędy, ale jest w tym samym stylu. Zostawiłem tutaj oryginalny pomysł do porównania.
Jon Betts

Szkoda, że ​​wciąż potrzebujesz tego CONST.prefiksu. To również będzie skomplikowane w sytuacjach wielomodułowych.
Alfe

1
Myślę, że zazwyczaj w tej sytuacji i tak chciałbyś pogrupować stałe w niektóre powiązane pakiety (zamiast mieć jeden gigantyczny obiekt CONST), więc prawdopodobnie nie jest to taka zła rzecz.
Jon Betts

Dlaczego ta odpowiedź jest wciąż tak daleko ?! __slots__Rozwiązanie jest tak elegancki i skuteczny. Ze wszystkiego, co przeczytałem, jest to tak blisko, jak to możliwe, do tworzenia stałych w Pythonie. Dziękuję Ci bardzo. I dla wszystkich zainteresowanych, oto genialne i dogłębne wyjaśnienie __slots__magii.
JohnGalt

34

Python nie ma stałych.

Być może najłatwiejszą alternatywą jest zdefiniowanie dla niej funkcji:

def MY_CONSTANT():
    return 42

MY_CONSTANT() teraz ma całą funkcjonalność stałej (plus kilka irytujących nawiasów klamrowych).


1
Chciałem tylko dodać tę sugestię, ale na szczęście przewinąłem w dół do nisko ocenianych odpowiedzi. Mam nadzieję, że będzie dalej oceniany i w pełni zgadzam się, że ma wszystkie funkcje stałej i jest bardzo prosty i bezpośredni. Patrząc na ilość kodu na płycie we wszystkich wyrafinowanych rozwiązaniach, uważam, że nawiasy klamrowe są względnie nieprzyjemne.
yaccob

1
jest to najprostsza odpowiedź, chociaż należy zauważyć, że ma pewne obciążenie i nie powstrzyma idiotów modyfikujących wartość zwracaną. Zapobiegnie to zmianie kodu w dalszej linii
MrMesees,

@MrMesees modyfikuje zwracaną wartość? Masz na myśli edycję źródła? Ale przed tym nie jesteś chroniony nawet w C ++, gdzie stałe (jak constexpr) są naprawdę twardymi stałymi.
Ruslan

@ Ruslan miałem na myśli to, że ponieważ python nie ma constexpr, nie zatrzyma edycji wartości po powrocie do kontekstu zewnętrznego. W tym przykładzie nic nie zrobiono 42 w celu wymuszenia stanu zamrożenia.
MrMesees

20

Oprócz dwóch najważniejszych odpowiedzi (po prostu używaj zmiennych z nazwami WIELKIMI LITERAMI lub używaj właściwości, aby wartości były tylko do odczytu), chcę wspomnieć, że można zastosować metaklasy w celu zaimplementowania nazwanych stałych. Podaję bardzo proste rozwiązanie wykorzystujące metaklasy w GitHub, które może być pomocne, jeśli chcesz, aby wartości były bardziej pouczające o ich typie / nazwie:

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

Jest to nieco bardziej zaawansowany Python, ale nadal bardzo łatwy w użyciu i przydatny. (Moduł ma kilka dodatkowych funkcji, w tym stałe tylko do odczytu, zobacz README.)

Istnieją podobne rozwiązania unoszące się w różnych repozytoriach, ale o ile mi wiadomo, brakuje im jednej z podstawowych cech, których oczekiwałbym od stałych (takich jak bycie stałym lub bycie dowolnego typu), lub mają cechy ezoteryczne, które dodały, że uczynić je mniej ogólnie obowiązującymi. Ale YMMV, byłbym wdzięczny za opinie. :-)


3
Podoba mi się twoja implementacja w GitHub. Byłem prawie gotowy do napisania podstawowej klasy, która zaimplementowała funkcję wyszukiwania wstecznego, ale widzę, że zrobiłeś to i wiele więcej!
Kerr

Dzięki, @Kerr, to moja pierwsza opinia, która mnie uszczęśliwiła. :-)
hans_meine

Niesamowite. Właśnie tego wypróbowałem. Miło mieć to jako opcję. Chociaż nie zdecydowałem, czy zależy mi wystarczająco na aspekcie tylko do odczytu, aby użyć tego zamiast po prostu robić def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), zgodnie z stackoverflow.com/a/1695250/199364 , sekcja „We wcześniejszych wersjach ...”
ToolmakerSteve

19

Właściwości są jednym ze sposobów tworzenia stałych. Możesz to zrobić, deklarując właściwość gettera, ale ignorując seter. Na przykład:

class MyFinalProperty(object):

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

Możesz przeczytać artykuł, który napisałem, aby znaleźć więcej sposobów korzystania z właściwości Pythona.


Niedocenione rozwiązanie. Właśnie to zaimplementowałem po znalezieniu tej strony (nie tej odpowiedzi) i zawróciłem, aby dodać ją, jeśli jeszcze nie. Chciałem podkreślić przydatność tej odpowiedzi.
Marc

18

Edycja: Dodano przykładowy kod dla Python 3

Uwaga: ta inna odpowiedź wygląda na to, że zapewnia znacznie bardziej kompletną implementację podobną do następującej (z większą liczbą funkcji).

Najpierw utwórz metaklasę :

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

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

Zapobiega to zmianie właściwości statyki. Następnie utwórz kolejną klasę, która korzysta z tej metaklasy:

class Const(object):
    __metaclass__ = MetaConst

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

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

Lub, jeśli używasz Python 3:

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

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

To powinno zapobiec zmianie rekwizytów instancji. Aby z niego skorzystać, odziedzicz:

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

Teraz rekwizyty, dostępne bezpośrednio lub poprzez instancję, powinny być stałe:

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

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

Oto przykład powyższego działania. Oto kolejny przykład dla Python 3.


10

Możesz użyć nazwanego tempa jako obejścia, aby skutecznie utworzyć stałą, która działa tak samo jak statyczna zmienna końcowa w Javie („stała” Javy). W miarę obejścia tego problemu jest to trochę eleganckie. (Bardziej eleganckim podejściem byłoby po prostu ulepszenie języka Python - jaki język pozwala ci na nowo zdefiniować math.pi? - ale ja dygresję).

(Pisząc to, zdaję sobie sprawę z innej odpowiedzi na wspomniane pytanie o nazwiepleple, ale kontynuuję tutaj, ponieważ pokażę składnię, która bardziej przypomina paralelę, której można się spodziewać w Javie, ponieważ nie trzeba tworzyć nazwanego wpisz, jak namedtuple zmusza cię do zrobienia.)

Idąc za twoim przykładem, będziesz pamiętać, że w Javie musimy zdefiniować stałą wewnątrz jakiejś klasy ; ponieważ nie wspomniałeś o nazwie klasy, nazwijmy ją Foo. Oto klasa Java:

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

Oto odpowiednik Pythona.

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

Kluczową kwestią, którą chcę tutaj dodać, jest to, że nie potrzebujesz osobnego Footypu (fajna byłaby „anonimowa krotka o nazwie”, choć brzmi to jak oksymoron), więc nazywamy naszą nazwę imienną, _Fooaby, miejmy nadzieję, nie będzie ucieczka do importowania modułów.

Drugą kwestią jest to, że natychmiast tworzymy wystąpienie nazwy, nazywając ją Foo; nie trzeba tego robić w osobnym kroku (chyba że chcesz). Teraz możesz robić to, co możesz robić w Javie:

>>> Foo.CONST_NAME
'Name'

Ale nie możesz do tego przypisać:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Potwierdzenie: Myślałem, że wymyśliłem nazwane podejście, ale potem widzę, że ktoś inny udzielił podobnej (choć mniej zwartej) odpowiedzi. Potem zauważyłem również, czym są „nazwane krotki” w Pythonie? , który wskazuje, że sys.version_infojest to teraz nazwa-imitacja, więc być może standardowa biblioteka Pythona już wymyśliła ten pomysł znacznie wcześniej.

Zauważ, że niestety (wciąż jest to Python) możesz Foocałkowicie usunąć całe zadanie:

>>> Foo = 'bar'

(facepalm)

Ale przynajmniej zapobiegamy Foo.CONST_NAMEzmianie wartości, a to lepsze niż nic. Powodzenia.


Dzięki za wprowadzenie metody namedtuple. Zdecydowanie innowacyjny. Może również okazać się, że mój „komentarz” jest istotny.
RayLuo

10

PEP 591 ma kwalifikator „końcowy”. Egzekwowanie należy do sprawdzania typu.

Możesz więc zrobić:

MY_CONSTANT: Final = 12407

Uwaga: Final słowo kluczowe dotyczy tylko wersji Python 3.8


9

Oto implementacja klasy „Constants”, która tworzy instancje z atrybutami tylko do odczytu (stałymi). Np. Można użyć, Nums.PIaby uzyskać wartość, która została zainicjowana jako 3.14159i Nums.PI = 22podnosi wyjątek.

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

Dzięki FrozenDict @MikeGraham , który wykorzystałem jako punkt wyjścia. Zmieniono, więc zamiast Nums['ONE']składni użycia jest Nums.ONE.

I dzięki odpowiedzi @ Raufio za pomysł na zastąpienie __ setattr __.

Lub implementację z większą funkcjonalnością, zobacz @Hans_meine's named_constants w GitHub


2
Python jest językiem wyrażającym zgodę dorosłych. Nie ma ochrony przed czymś takim. Nums._d['PI'] = 22 Uważam, że sam język nie zapewnia żadnego sposobu oznaczania rzeczy jako niemodyfikowalnych.
Ajay M

8

Krotka technicznie kwalifikuje się jako stała, ponieważ krotka spowoduje błąd, jeśli spróbujesz zmienić jedną z jej wartości. Jeśli chcesz zadeklarować krotkę z jedną wartością, umieść przecinek po jej jedynej wartości, jak poniżej:

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

Aby sprawdzić wartość tej zmiennej, użyj czegoś podobnego do tego:

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

Jeśli spróbujesz zmienić tę wartość, pojawi się błąd.


7

Zrobiłbym klasę, która zastąpi __setattr__metodę podstawowej klasy obiektowej i zawinie w nią moje stałe, zauważ, że używam Pythona 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)

Aby zawinąć ciąg:

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

Jest to dość proste, ale jeśli chcesz używać swoich stałych w taki sam sposób, jak w przypadku niestałego obiektu (bez użycia constObj.value), będzie to nieco bardziej intensywne. Możliwe, że może to powodować problemy, więc najlepiej pozostawić .valuepokaz i wiedzieć, że wykonujesz operacje ze stałymi (być może nie w najbardziej „pythoniczny” sposób).


+1 za ciekawe podejście. Chociaż nie tak czyste, jak odpowiedzi, które już zostały udzielone. I nawet najprostsze sugerowane wcześniej rozwiązanie def ONE(): return 1jest łatwiejsze w użyciu ONE()niż ta odpowiedź ONE.value.
ToolmakerSteve,

7

Niestety, Python nie ma jeszcze stałych i szkoda. ES6 już dodał stałe obsługi JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ), ponieważ jest to bardzo przydatna rzecz w każdym języku programowania. Zgodnie z odpowiedziami w innych odpowiedziach w środowisku Python używaj konwencji - zmiennej wielkiej litery użytkownika jako stałych, ale nie chroni ona przed przypadkowymi błędami w kodzie. Jeśli chcesz, może okazać się przydatne rozwiązanie z jednym plikiem jako następne (zobacz dokumentację, jak go używać).

plik 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()

Jeśli to nie wystarczy, zobacz pełną instrukcję dla tego.

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

Zalety: 1. Dostęp do wszystkich stałych dla całego projektu 2. Ścisła kontrola wartości stałych

Brakuje: 1. Brak obsługi niestandardowych typów i typu „dict”

Uwagi:

  1. Testowane z Python3.4 i Python3.5 (używam do tego „toksyny”)

  2. Środowisko testowe:

.

$ 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

Możesz to nieco poprawić, automatycznie konwertując słowniki na nazwane krotki
Peter Schorn

6

Pythoński sposób deklarowania „stałych” jest w zasadzie zmienną na poziomie modułu:

RED = 1
GREEN = 2
BLUE = 3

A potem napisz swoje klasy lub funkcje. Ponieważ stałe prawie zawsze są liczbami całkowitymi i są również niezmienne w Pythonie, masz bardzo małą szansę na ich zmianę.

Chyba że, jeśli wyraźnie to ustawisz RED = 2.


21
Tak, ale blokowanie możliwości „jawnego ustawienia RED = 2” to cała korzyść (w innych językach) z możliwości deklarowania nazwy zmiennej jako „stałej”!
ToolmakerSteve,

6
Czy zyskałbyś na blokowaniu tego? Najbardziej użyteczną rzeczą w const jest zwykle optymalizacja kompilatora, która tak naprawdę nie jest rzeczą w Pythonie. Chcesz, aby coś było stałe? Tylko tego nie zmieniaj. Jeśli martwisz się, że ktoś to zmieni, możesz po prostu wyłączyć go z zakresu lub po prostu uświadomić sobie, że jeśli ktoś to zmienia, to ich problem i on musi sobie z tym poradzić, a nie ty.
Kevin

@Kevin: „ Czy zyskałbyś ... ”, korzyść z staticposiadania jednego magazynu dla wartości dla wszystkich instancji klasy? Chyba że istnieje możliwość zadeklarowania zmiennej statycznej / klasy.
min.

8
Główny problem polega na tym, że niektórzy mogą postrzegać ją jako wartość, która jest źródłem prawdy, której nie można zmienić, i używają jej jako źródła prawdy w całym kodzie zamiast wprowadzania magicznych wartości (co często widzę w Pythonie) - a inni mogą postrzegać to jako coś, co wolno im zmieniać. Gdy ktoś zmienia zmienną globalną i nie można powiedzieć, gdzie została zmieniona, a aplikacja ulega awarii, ponieważ RED = „niebieski” zamiast „czerwony”, wprowadzasz całkowicie niepotrzebny problem, który został już rozwiązany tak prosto i jest powszechnie rozumiany.
Dagrooms

5

Możemy stworzyć obiekt deskryptora.

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) Jeśli chcielibyśmy pracować ze stałymi na poziomie instancji, to:

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) jeśli chcielibyśmy tworzyć stałe tylko na poziomie klasy, moglibyśmy użyć metaklasy, która służy jako pojemnik dla naszych stałych (naszych obiektów deskryptorów); wszystkie klasy, które zejdą, odziedziczą nasze stałe (nasze obiekty deskryptorów) bez żadnego ryzyka, które można zmodyfikować.

# 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

Jeśli utworzę podklasę Foo, klasa ta odziedziczy stałą bez możliwości ich modyfikacji

class Bar(Foo): pass

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

4

Słowniki w języku Python są zmienne, więc nie wydają się dobrym sposobem deklarowania stałych:

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

4

Oto sztuczka, jeśli chcesz stałych i nie przejmujesz się ich wartościami:

Po prostu zdefiniuj puste klasy.

na przykład:

class RED: 
    pass
class BLUE: 
    pass

4

W Pythonie stała jest po prostu zmienną o nazwie pisanej wielkimi literami, ze słowami oddzielonymi znakiem podkreślenia,

na przykład

DAYS_IN_WEEK = 7

Wartość jest zmienna, ponieważ możesz ją zmienić. Ale skoro zasady dotyczące nazwy mówią ci, że jest stała, dlaczego miałbyś? W końcu to twój program!

Takie podejście jest stosowane w całym pythonie. Nie ma privatesłowa kluczowego z tego samego powodu. Poprzedź nazwę znakiem podkreślenia i wiesz, że ma ona być prywatna. Kod może złamać regułę ... tak jak programista może usunąć prywatne słowo kluczowe.

Python mógł dodać const słowo kluczowe ... ale programista może usunąć słowo kluczowe, a następnie zmienić stałą, jeśli chce, ale dlaczego to zrobić? Jeśli chcesz złamać regułę, możesz ją zmienić i tak. Ale po co zawracać sobie głowę łamaniem reguły, jeśli nazwa wyjaśnia intencję?

Może jest jakiś test jednostkowy, w którym sensowne jest zastosowanie zmiany wartości? Aby zobaczyć, co dzieje się przez 8 dni w tygodniu, nawet jeśli w prawdziwym świecie nie można zmienić liczby dni w tygodniu. Jeśli język powstrzymał cię przed zrobieniem wyjątku, jeśli istnieje tylko jeden przypadek, musisz złamać regułę ... wtedy musiałbyś przestać deklarować ją jako stałą, nawet jeśli nadal jest stała w aplikacji, i jest tylko ten jeden przypadek testowy, który widzi, co się stanie, jeśli zostanie zmieniony.

Nazwa wielkimi literami mówi, że ma ona być stała. To jest ważne. Nie jest to język wymuszający ograniczenia w kodzie, który i tak możesz zmienić.

Taka jest filozofia Pythona.


4

Nie ma na to idealnego sposobu. Jak rozumiem, większość programistów po prostu używa tego identyfikatora, więc PI = 3,142 można łatwo zrozumieć jako stałą.

Z drugiej strony, jeśli chcesz czegoś, co faktycznie działa jak stała, nie jestem pewien, czy to znajdziesz. Cokolwiek zrobisz, zawsze będzie jakiś sposób edycji „stałej”, więc tak naprawdę nie będzie to stała. Oto bardzo prosty, brudny przykład:

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

Wygląda na to, że sprawi, że będzie stały w stylu PHP.

W rzeczywistości wystarczy, aby ktoś zmienił wartość:

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

To samo dotyczy wszystkich innych rozwiązań, które tu znajdziesz - nawet tych sprytnych, które tworzą klasę i redefiniują metodę ustawiania atrybutu - zawsze będzie na to sposób. Taki właśnie jest Python.

Radzę, aby po prostu uniknąć wszelkich kłopotów i po prostu wykorzystać swoje identyfikatory. To naprawdę nie byłaby właściwa stała, ale z drugiej strony nic by nie zrobiło.


4

Jest łatwiejszy sposób na wykonanie tego za pomocą namedtuple:

from collections import namedtuple


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

Przykład użycia

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

Dzięki takiemu podejściu możesz nazywać swoje stałe stałą.


Wszystkim, którzy to czytają, pamiętaj, że jeśli ustawisz zmienny obiekt jako jedną z tych stałych, każdy może zmienić jego wartość wewnętrzną. na przykład, pozwól bar = [1, 2, 3], a następnie możesz zrobić w następujący sposób: CONSTS.bar [1] = 'a' i nie zostanie odrzucony. Uważaj więc na to.
Juan Ignacio Sánchez

Zamiast tej zuchwałej metody, którą stworzyłem dla zabawy, polecam zamiast tego użyć dekoratora właściwości Pythona.
Juan Ignacio Sánchez

4

Może pomoże ci biblioteka pconst ( 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.


3

Możesz użyć StringVar lub IntVar itp., Twoja stała to const_val

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

def reverse(*args):
    const_val.set(val)

2

Możesz to zrobić za pomocą collections.namedtuplei 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

2

(Ten akapit miał być komentarzem do tych odpowiedzi tu i tam , w których wspomnianonamedtuple , ale robi się zbyt długo, aby zmieścić się w komentarzu, więc proszę bardzo.)

Wspomniane wyżej wspomniane podejście jest zdecydowanie innowacyjne. Jednak ze względu na kompletność, na końcu sekcji NamedTuple w oficjalnej dokumentacji czytamy:

Wyliczone stałe można zaimplementować za pomocą krotek nazwanych, ale prostsze i wydajniejsze jest użycie prostej deklaracji klasy:

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

Innymi słowy, oficjalna dokumentacja woli raczej stosować praktyczny sposób niż faktyczne zachowanie tylko do odczytu. Wydaje mi się, że to kolejny przykład Zen Pythona :

Prosty jest lepszy niż złożony.

praktyczność pokonuje czystość.


2

Oto zbiór idiomów, które stworzyłem jako próbę ulepszenia niektórych z już dostępnych odpowiedzi.

Wiem, że użycie stałej nie jest pytoniczne i nie powinieneś tego robić w domu!

Jednak Python jest tak dynamicznym językiem! To forum pokazuje, jak możliwe jest tworzenie konstrukcji, które wyglądają i zachowują się jak stałe. Ta odpowiedź ma na celu przede wszystkim zbadanie, co może wyrazić język.

Proszę, nie bądźcie dla mnie zbyt surowi :-).

Aby uzyskać więcej informacji, napisałem blog akompaniamentu na temat tych idiomów .

W tym poście wywołam zmienną stałą do stałego odniesienia do wartości (niezmiennej lub innej). Co więcej, mówię, że zmienna ma zamrożoną wartość, gdy odwołuje się do obiektu zmiennego, że kod klienta nie może zaktualizować swoich wartości.

Przestrzeń stałych (SpaceConstants)

Ten idiom tworzy przestrzeń nazw zmiennych stałych (aka SpaceConstants). Jest to modyfikacja fragmentu kodu autorstwa Alexa Martellego, aby uniknąć użycia obiektów modułowych. W szczególności ta modyfikacja korzysta z tego, co nazywam fabryką klas, ponieważ w ramach funkcji SpaceConstants , klasa o nazwie SpaceConstants definiowana jest jest jej instancja.

Zbadałem wykorzystanie fabryki klas do implementacji opartego na zasadach projektowania podobnego do projektu w Pythonie w Stackoverflow, a także w blogu .

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"

Przestrzeń zamrożonych wartości (SpaceFrozenValues)

Ten następny idiom jest modyfikacją SpaceConstants, w której zamrażane są odnośne zmienne obiekty. Ta implementacja wykorzystuje to, co nazywam zamknięciem współdzielonym między funkcjami setattr i getattr . Wartość zmiennego obiektu jest kopiowana i przywoływana przez zmienną pamięć podręczną zdefiniowaną wewnątrz zamknięcia współdzielonego z funkcją. Tworzy to, co nazywam chronioną przed zamknięciem kopią obiektu podlegającego zmianom .

Musisz być ostrożny w używaniu tego idiomu, ponieważ getattr zwraca wartość pamięci podręcznej, wykonując głęboką kopię. Ta operacja może mieć znaczący wpływ na wydajność dużych obiektów!

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"

Stała przestrzeń (ConstantSpace)

Ten idiom jest niezmienną przestrzenią nazw zmiennych stałych lub ConstantSpace . Jest to kombinacja odpowiedzi niesamowicie prostego Jon Betts' w stackoverflow z fabryki klasy .

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

Zamrożone miejsce (FrozenSpace)

Ten idiom jest niezmienną przestrzenią nazw zamrożonych zmiennych lub FrozenSpace . Wywodzi się z poprzedniego wzorca, czyniąc każdą zmienną właściwością chronioną przez zamknięcie wygenerowanej klasy FrozenSpace .

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"

2

W Pythonie stałe nie istnieją, ale można wskazać, że zmienna jest stałą i nie można jej zmieniać, dodając CONST_na początku nazwy zmiennej i stwierdzając, że jest stała w komentarzu:

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.

Alternatywnie możesz utworzyć funkcję, która działa jak stała:

def CONST_daysInWeek():
    return 7;

1

W moim przypadku potrzebowałem niezmiennych bajtów do implementacji biblioteki kryptograficznej zawierającej wiele literałów, które chciałem zapewnić, aby były stałe.

Ta odpowiedź działa, ale próba ponownego przypisania elementów bytearray nie powoduje błędu.

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

Stałe są niezmienne, ale ciągłe przypisywanie bajtów nie powiedzie się po cichu:

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

Bardziej wydajne, proste, a może nawet bardziej „pythonowe” podejście polega na użyciu obiektów podglądu pamięci (obiektów buforowych w <= 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)

Przypisanie elementu ConstArray to 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

1

Piszę util lib dla python const: kkconst - pypi support str, int, float, datetime

instancja const const zachowa swoje zachowanie typu podstawowego.

Na przykład:

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

więcej szczegółów użycia można przeczytać adres URL pypi : pypi lub github


1

Możesz owinąć stałą w tablicę numpy, oflagować ją tylko do zapisu i zawsze wywoływać ją przez indeks 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

oczywiście chroni to tylko zawartość numpy, a nie samą zmienną „CONSTANT”; nadal możesz zrobić:

CONSTANT = 'foo'

i CONSTANTzmieniłoby się, ale to szybko spowodowałoby błąd TypeError, gdy po raz pierwszy CONSTANT[0]zostanie wywołany w skrypcie.

chociaż ... Przypuszczam, że jeśli kiedyś to zmieniłeś

CONSTANT = [1,2,3]

teraz nie dostaniesz już TypeError. hmmmm ....

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.