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:
classFoo(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 importFinal
a:Final=1# Executes fine, but mypy will report an error if you run mypy on this:
a =2
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
Pamiętaj, że dekorator @apply wydaje się przestarzały.
Aby zdefiniować identyfikator FOO, jodły definiują dwie funkcje (fset, fget - nazwy są do wyboru).
Następnie użyj wbudowanej propertyfunkcji, aby skonstruować obiekt, który można „ustawić” lub „uzyskać”.
Uwaga property: pierwsze dwa parametry funkcji są nazwane fseti fget.
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
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 =3Traceback(most recent call last):File"<stdin>", line 1,in<module>AttributeError: can't set attribute
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:
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.
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():return42
MY_CONSTANT() teraz ma całą funkcjonalność stałej (plus kilka irytujących nawiasów klamrowych).
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 importConstants>>>classColors(Constants):... black =0... red =1... white =15...>>> c =Colors.black
>>> c ==0True>>> c
Colors.black
>>> c.name()'black'>>>Colors(0)is c
True
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. :-)
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:
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).
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 classFoo{
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.
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 ----------classConstants(object):"""
Create objects with read-only (constant) attributes.
Example:
Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
print 10 + Nums.PI
print '----- Following line is deliberate ValueError -----'
Nums.PI = 22
"""def __init__(self,*args,**kwargs):
self._d = dict(*args,**kwargs)def __iter__(self):return iter(self._d)def __len__(self):return len(self._d)# NOTE: This is only called if self lacks the attribute.# So it does not interfere with get of 'self._d', etc.def __getattr__(self, name):return self._d[name]# ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.#If use as keys, they won't be constant.def __setattr__(self, name, value):if(name[0]=='_'):
super(Constants, self).__setattr__(name, value)else:raiseValueError("setattr while locked", self)if(__name__ =="__main__"):# Usage example.Nums=Constants(ONE=1, PI=3.14159,DefaultWidth=100.0)print10+Nums.PI
print'----- Following line is deliberate ValueError -----'Nums.PI =22
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 __.
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.
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):raiseValueError("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',)classConstant(object):"""
Implementation strict constants in Python 3.
A constant can be set up, but can not be changed or deleted.
Value of constant may any immutable type, as well as list or set.
Besides if value of a constant is list or set, it will be converted in an immutable type as next:
list -> tuple
set -> frozenset
Dict as value of a constant has no support.
>>> const = Constant()
>>> del const.temp
Traceback (most recent call last):
NameError: name 'temp' is not defined
>>> const.temp = 1
>>> const.temp = 88
Traceback (most recent call last):
...
TypeError: Constanst can not be changed
>>> del const.temp
Traceback (most recent call last):
...
TypeError: Constanst can not be deleted
>>> const.I = ['a', 1, 1.2]
>>> print(const.I)
('a', 1, 1.2)
>>> const.F = {1.2}
>>> print(const.F)
frozenset([1.2])
>>> const.D = dict()
Traceback (most recent call last):
...
TypeError: dict can not be used as constant
>>> del const.UNDEFINED
Traceback (most recent call last):
...
NameError: name 'UNDEFINED' is not defined
>>> const()
{'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
"""def __setattr__(self, name, value):"""Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
If the constant already exists, then made prevent againt change it."""if name in self.__dict__:raiseTypeError('Constanst can not be changed')ifnot isinstance(value, collections.Hashable):if isinstance(value, list):
value = tuple(value)elif isinstance(value, set):
value = frozenset(value)elif isinstance(value, dict):raiseTypeError('dict can not be used as constant')else:raiseValueError('Muttable or custom type is not supported')
self.__dict__[name]= value
def __delattr__(self, name):"""Deny against deleting a declared constant."""if name in self.__dict__:raiseTypeError('Constanst can not be deleted')raiseNameError("name '%s' is not defined"% name)def __call__(self):"""Return all constans."""return self.__dict__
const =Constant()if __name__ =='__main__':import doctest
doctest.testmod()
Jeśli to nie wystarczy, zobacz pełną instrukcję dla tego.
import decimal
import uuid
import datetime
import unittest
from..constants importConstantclassTestConstant(unittest.TestCase):"""
Test for implementation constants in the Python
"""def setUp(self):
self.const =Constant()def tearDown(self):del self.const
def test_create_constant_with_different_variants_of_name(self):
self.const.CONSTANT =1
self.assertEqual(self.const.CONSTANT,1)
self.const.Constant=2
self.assertEqual(self.const.Constant,2)
self.const.ConStAnT=3
self.assertEqual(self.const.ConStAnT,3)
self.const.constant =4
self.assertEqual(self.const.constant,4)
self.const.co_ns_ta_nt =5
self.assertEqual(self.const.co_ns_ta_nt,5)
self.const.constant1111 =6
self.assertEqual(self.const.constant1111,6)def test_create_and_change_integer_constant(self):
self.const.INT =1234
self.assertEqual(self.const.INT,1234)with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.INT =.211def test_create_and_change_float_constant(self):
self.const.FLOAT =.1234
self.assertEqual(self.const.FLOAT,.1234)with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.FLOAT =.211def test_create_and_change_list_constant_but_saved_as_tuple(self):
self.const.LIST =[1,.2,None,True, datetime.date.today(),[],{}]
self.assertEqual(self.const.LIST,(1,.2,None,True, datetime.date.today(),[],{}))
self.assertTrue(isinstance(self.const.LIST, tuple))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.LIST =.211def test_create_and_change_none_constant(self):
self.const.NONE =None
self.assertEqual(self.const.NONE,None)with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.NONE =.211def test_create_and_change_boolean_constant(self):
self.const.BOOLEAN =True
self.assertEqual(self.const.BOOLEAN,True)with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.BOOLEAN =Falsedef test_create_and_change_string_constant(self):
self.const.STRING ="Text"
self.assertEqual(self.const.STRING,"Text")with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.STRING +='...'with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.STRING ='TEst1'def test_create_dict_constant(self):with self.assertRaisesRegexp(TypeError,'dict can not be used as constant'):
self.const.DICT ={}def test_create_and_change_tuple_constant(self):
self.const.TUPLE =(1,.2,None,True, datetime.date.today(),[],{})
self.assertEqual(self.const.TUPLE,(1,.2,None,True, datetime.date.today(),[],{}))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.TUPLE ='TEst1'def test_create_and_change_set_constant(self):
self.const.SET ={1,.2,None,True, datetime.date.today()}
self.assertEqual(self.const.SET,{1,.2,None,True, datetime.date.today()})
self.assertTrue(isinstance(self.const.SET, frozenset))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.SET =3212def test_create_and_change_frozenset_constant(self):
self.const.FROZENSET = frozenset({1,.2,None,True, datetime.date.today()})
self.assertEqual(self.const.FROZENSET, frozenset({1,.2,None,True, datetime.date.today()}))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.FROZENSET =Truedef test_create_and_change_date_constant(self):
self.const.DATE = datetime.date(1111,11,11)
self.assertEqual(self.const.DATE, datetime.date(1111,11,11))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.DATE =Truedef test_create_and_change_datetime_constant(self):
self.const.DATETIME = datetime.datetime(2000,10,10,10,10)
self.assertEqual(self.const.DATETIME, datetime.datetime(2000,10,10,10,10))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.DATETIME =Nonedef test_create_and_change_decimal_constant(self):
self.const.DECIMAL = decimal.Decimal(13123.12312312321)
self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.DECIMAL =Nonedef test_create_and_change_timedelta_constant(self):
self.const.TIMEDELTA = datetime.timedelta(days=45)
self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.TIMEDELTA =1def test_create_and_change_uuid_constant(self):
value = uuid.uuid4()
self.const.UUID = value
self.assertEqual(self.const.UUID, value)with self.assertRaisesRegexp(TypeError,'Constanst can not be changed'):
self.const.UUID =[]def test_try_delete_defined_const(self):
self.const.VERSION ='0.0.1'with self.assertRaisesRegexp(TypeError,'Constanst can not be deleted'):del self.const.VERSION
def test_try_delete_undefined_const(self):with self.assertRaisesRegexp(NameError,"name 'UNDEFINED' is not defined"):del self.const.UNDEFINED
def test_get_all_defined_constants(self):
self.assertDictEqual(self.const(),{})
self.const.A =1
self.assertDictEqual(self.const(),{'A':1})
self.const.B ="Text"
self.assertDictEqual(self.const(),{'A':1,'B':"Text"})
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:
Testowane z Python3.4 i Python3.5 (używam do tego „toksyny”)
Środowisko testowe:
.
$ uname -a
Linux wlysenko-Aspire3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
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ę.
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.
classConstant:def __init__(self,value=None):
self.value = value
def __get__(self,instance,owner):return self.value
def __set__(self,instance,value):raiseValueError("You can't change a constant")
1) 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 =100Traceback(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 FooclassFooMeta(type):pass# class FooclassFoo(metaclass=FooMeta):pass# I create constants in my metaclassFooMeta.NUM =Constant(0xff)FooMeta.NAME =Constant('FOO')>>>Foo.NUM #=> 255>>>Foo.NAME #=> 'FOO'>>>Foo.NUM =0#=> ValueError: You can't change a constant
Jeśli utworzę podklasę Foo, klasa ta odziedziczy stałą bez możliwości ich modyfikacji
classBar(Foo):pass>>>Bar.NUM #=> 255>>>Bar.NUM =0#=> ValueError: You can't change a constant
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ć.
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:
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.
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.
(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:
classStatus:
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 :
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.
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 .
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
defSpaceFrozenValues():
cache ={}def setattr(self, name, value):nonlocal cache
if name in cache:raiseAttributeError("Cannot reassign members")
cache[name]= deepcopy(value)def getattr(self, name):nonlocal cache
if name notin cache:raiseAttributeError("Object has no attribute '{}'".format(name))return deepcopy(cache[name])
cls = type('SpaceFrozenValues',(),{'__getattr__': getattr,'__setattr__': setattr
})return cls()
fv =SpaceFrozenValues()print(fv.x)# AttributeError: Object has no attribute 'x'
fv.x =2# bind attribute xprint(fv.x)# print "2"
fv.x =3# raise "AttributeError: Cannot reassign members"
fv.y ={'name':'y','value':2}# bind attribute yprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y['name']='yprime'# you can try to change mutable objectsprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y ={}# raise "AttributeError: Cannot reassign members"
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 .
defConstantSpace(**args):
args['__slots__']=()
cls = type('ConstantSpace',(), args)return cls()
cs =ConstantSpace(
x =2,
y ={'name':'y','value':2})print(cs.x)# print "2"
cs.x =3# raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"print(cs.y)# print "{'name': 'y', 'value': 2}"
cs.y['name']='yprime'# mutable object can be changedprint(cs.y)# print "{'name': 'yprime', 'value': 2}"
cs.y ={}# raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z =3# raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"
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
defFreezeProperty(value):
cache = deepcopy(value)return property(lambda self: deepcopy(cache))defFrozenSpace(**args):
args ={k:FreezeProperty(v)for k, v in args.items()}
args['__slots__']=()
cls = type('FrozenSpace',(), args)return cls()
fs =FrozenSpace(
x =2,
y ={'name':'y','value':2})print(fs.x)# print "2"
fs.x =3# raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"print(fs.y)# print "{'name': 'y', 'value': 2}"
fs.y['name']='yprime'# try to change mutable objectprint(fs.y)# print "{'name': 'y', 'value': 2}"
fs.y ={}# raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z =3# raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
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:
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`'''classConstError(TypeError):'''special exception for const reassignment'''passraiseConstErrordef fget(self):'''get a const'''return func()return property(fget, fset)classConsts(object):'''contain all constants'''@constdef C1():'''reassignment to C1 fails silently'''return bytearray.fromhex('deadbeef')@constdef pi():'''is immutable'''return3.141592653589793
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
raiseConstError
__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:raiseNotImplementedErrorelif int(PY_VER[1])==6:
memoryview = buffer
classConstArray(object):'''represent a constant bytearray'''def __init__(self, init):'''
create a hidden bytearray and expose a memoryview of that bytearray for
read-only use
'''if int(PY_VER[1])==6:
self.__array = bytearray(init.decode('hex'))else:
self.__array = bytearray.fromhex(init)
self.array = memoryview(self.__array)def __str__(self):return str(self.__array)def __getitem__(self,*args,**kwargs):return self.array.__getitem__(*args,**kwargs)
Przypisanie elementu ConstArray to TypeError:
>>> C1 =ConstArray('deadbeef')>>> C1[0]=0Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError:'ConstArray' object does not support item assignment
>>> C1[0]222
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,)classMathConst(BaseConst):
PI =ConstFloatField(3.1415926, verbose_name=u"Pi")
E =ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")# Euler's number"
GOLDEN_RATIO =ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")
magic_num =MathConst.GOLDEN_RATIO
assert isinstance(magic_num,ConstFloatField)assert isinstance(magic_num, float)print(magic_num)# 0.6180339887print(magic_num.verbose_name)# Golden Ratio
więcej szczegółów użycia można przeczytać adres URL pypi :
pypi lub github
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ś
True=False
, a potem(2+2==4)==True
wracaFalse
.SyntaxError: can't assign to keyword
. To wydaje się być dobrą rzeczą.Odpowiedzi:
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:
jeśli nie, to po prostu
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.Final
adnotacja zmiennej, która powie statycznym kontrolerom typu (takim jak mypy), że zmienna nie powinna zostać przypisana ponownie. Jest to najbliższy odpowiednik Javyfinal
. Jednak w rzeczywistości nie zapobiega to zmianie przypisania :źródło
def MY_CONST_VALUE(): return 123
name.attribute
i mogą zawierać dowolną wartość. Deklaracja jest łatwaNums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
, użycie jest prosteprint 10 + Nums.PI
, próba zmiany wyników w wyjątkuNums.PI = 22
=> ValueError (..).Nie ma
const
sł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
Objaśnienie kodu:
constant
która pobiera wyrażenie i używa go do skonstruowania „gettera” - funkcji, która wyłącznie zwraca wartość wyrażenia.constant
funkcji, 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)
Pamiętaj, że dekorator @apply wydaje się przestarzały.
property
funkcji, aby skonstruować obiekt, który można „ustawić” lub „uzyskać”.property
: pierwsze dwa parametry funkcji są nazwanefset
ifget
.property
funkcjiźródło
AttributeError
iTypeError
myślę, że podniesiony wyjątek powinien być nowym błędem, który proponuję nazwaćConstantError
lub czymś takim, co jest podklasąTypeError
. Sekcja w dokumentach, która każe mi myśleć, że: docs.python.org/2/library/exceptions.htmlCONST.__dict__['FOO'] = 7
W Pythonie zamiast języka wymuszającego coś, ludzie używają konwencji nazewnictwa np.
__method
Dla metod prywatnych i_method
chronionych.W ten sam sposób możesz po prostu zadeklarować stałą jak wszystkie wielkie litery np
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
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:
źródło
def MY_CONSTANT(): return "one"
nie powstrzyma kogoś, później w kodzie, zrobieniaMY_CONSTANT = "two"
(lub ponownego opublikowania funkcji).MY_CONSTANT = MY_CONSTANT()
, ale o używanieMY_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ą.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__
: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:
Utworzenie instancji pozwala magicznej
__setattr__
metodzie na rozpoczęcie i przechwycenie prób ustawieniaFOO
zmiennej. 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
CONST
obiektu. Mając wyższą klasę, nazwa klasy również wydaje się nieco ponura, ale myślę, że jest ogólnie zwięzła.źródło
CONST.
prefiksu. To również będzie skomplikowane w sytuacjach wielomodułowych.__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.Python nie ma stałych.
Być może najłatwiejszą alternatywą jest zdefiniowanie dla niej funkcji:
MY_CONSTANT()
teraz ma całą funkcjonalność stałej (plus kilka irytujących nawiasów klamrowych).źródło
constexpr
) są naprawdę twardymi stałymi.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:
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. :-)
źródło
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 ...”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:
Możesz przeczytać artykuł, który napisałem, aby znaleźć więcej sposobów korzystania z właściwości Pythona.
źródło
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ę :
Zapobiega to zmianie właściwości statyki. Następnie utwórz kolejną klasę, która korzysta z tej metaklasy:
Lub, jeśli używasz Python 3:
To powinno zapobiec zmianie rekwizytów instancji. Aby z niego skorzystać, odziedzicz:
Teraz rekwizyty, dostępne bezpośrednio lub poprzez instancję, powinny być stałe:
Oto przykład powyższego działania. Oto kolejny przykład dla Python 3.
źródło
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:Oto odpowiednik Pythona.
Kluczową kwestią, którą chcę tutaj dodać, jest to, że nie potrzebujesz osobnego
Foo
typu (fajna byłaby „anonimowa krotka o nazwie”, choć brzmi to jak oksymoron), więc nazywamy naszą nazwę imienną,_Foo
aby, 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:Ale nie możesz do tego przypisać:
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_info
jest 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
Foo
całkowicie usunąć całe zadanie:(facepalm)
Ale przynajmniej zapobiegamy
Foo.CONST_NAME
zmianie wartości, a to lepsze niż nic. Powodzenia.źródło
PEP 591 ma kwalifikator „końcowy”. Egzekwowanie należy do sprawdzania typu.
Możesz więc zrobić:
Uwaga:
Final
słowo kluczowe dotyczy tylko wersji Python 3.8źródło
Oto implementacja klasy „Constants”, która tworzy instancje z atrybutami tylko do odczytu (stałymi). Np. Można użyć,
Nums.PI
aby uzyskać wartość, która została zainicjowana jako3.14159
iNums.PI = 22
podnosi wyjątek.Dzięki FrozenDict @MikeGraham , który wykorzystałem jako punkt wyjścia. Zmieniono, więc zamiast
Nums['ONE']
składni użycia jestNums.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
źródło
Nums._d['PI'] = 22
Uważam, że sam język nie zapewnia żadnego sposobu oznaczania rzeczy jako niemodyfikowalnych.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:
Aby sprawdzić wartość tej zmiennej, użyj czegoś podobnego do tego:
Jeśli spróbujesz zmienić tę wartość, pojawi się błąd.
źródło
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:Aby zawinąć ciąg:
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ć
.value
pokaz i wiedzieć, że wykonujesz operacje ze stałymi (być może nie w najbardziej „pythoniczny” sposób).źródło
def ONE(): return 1
jest łatwiejsze w użyciuONE()
niż ta odpowiedźONE.value
.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
Jeśli to nie wystarczy, zobacz pełną instrukcję dla tego.
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:
Testowane z Python3.4 i Python3.5 (używam do tego „toksyny”)
Środowisko testowe:
.
źródło
Pythoński sposób deklarowania „stałych” jest w zasadzie zmienną na poziomie modułu:
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
.źródło
RED = 2
” to cała korzyść (w innych językach) z możliwości deklarowania nazwy zmiennej jako „stałej”!static
posiadania jednego magazynu dla wartości dla wszystkich instancji klasy? Chyba że istnieje możliwość zadeklarowania zmiennej statycznej / klasy.Możemy stworzyć obiekt deskryptora.
1) Jeśli chcielibyśmy pracować ze stałymi na poziomie instancji, to:
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ć.
Jeśli utworzę podklasę Foo, klasa ta odziedziczy stałą bez możliwości ich modyfikacji
źródło
Słowniki w języku Python są zmienne, więc nie wydają się dobrym sposobem deklarowania stałych:
źródło
Oto sztuczka, jeśli chcesz stałych i nie przejmujesz się ich wartościami:
Po prostu zdefiniuj puste klasy.
na przykład:
źródło
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
private
sł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.
źródło
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:
Wygląda na to, że sprawi, że będzie stały w stylu PHP.
W rzeczywistości wystarczy, aby ktoś zmienił wartość:
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.
źródło
Jest łatwiejszy sposób na wykonanie tego za pomocą namedtuple:
Przykład użycia
Dzięki takiemu podejściu możesz nazywać swoje stałe stałą.
źródło
Może pomoże ci biblioteka pconst ( github ).
$ pip install pconst
[Out] Constant value of "APPLE_PRICE" is not editable.
źródło
Możesz użyć StringVar lub IntVar itp., Twoja stała to const_val
źródło
Możesz to zrobić za pomocą
collections.namedtuple
iitertools
:źródło
(Ten akapit miał być komentarzem do tych odpowiedzi tu i tam , w których wspomniano
namedtuple
, 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:
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 :
źródło
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 .
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!
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 .
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 .
źródło
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:Alternatywnie możesz utworzyć funkcję, która działa jak stała:
źródło
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.
Stałe są niezmienne, ale ciągłe przypisywanie bajtów nie powiedzie się po cichu:
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).
Przypisanie elementu ConstArray to
TypeError
:źródło
Piszę util lib dla python const: kkconst - pypi support str, int, float, datetime
instancja const const zachowa swoje zachowanie typu podstawowego.
Na przykład:
więcej szczegółów użycia można przeczytać adres URL pypi : pypi lub github
źródło
Możesz owinąć stałą w tablicę numpy, oflagować ją tylko do zapisu i zawsze wywoływać ją przez indeks zero.
oczywiście chroni to tylko zawartość numpy, a nie samą zmienną „CONSTANT”; nadal możesz zrobić:
i
CONSTANT
zmieniłoby się, ale to szybko spowodowałoby błąd TypeError, gdy po raz pierwszyCONSTANT[0]
zostanie wywołany w skrypcie.chociaż ... Przypuszczam, że jeśli kiedyś to zmieniłeś
teraz nie dostaniesz już TypeError. hmmmm ....
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html
źródło