Wie erstelle ich eine Konstante in Python?

990

Gibt es eine Möglichkeit, eine Konstante in Python zu deklarieren? In Java können wir auf folgende Weise konstante Werte erstellen:

public static final String CONST_NAME = "Name";

Was entspricht der obigen Java-Konstantendeklaration in Python?

zfranciscus
quelle
6
Tatsächlich ist die Möglichkeit, schreibgeschützte Variablen zu erstellen, über die Eigenschaftsfunktion / den Dekorator von Python möglich . Die Antwort von inv ist ein Beispiel für eine benutzerdefinierte Verwendung davon. Eigenschaft ist allgemeiner als das, aber eine gute Analyse, wie es funktioniert, ist auf Shalabh Chaturvedis Python-Attribute und -Methoden .
n611x007
20
IMHO ist das Erzwingen von Konstanz "nicht pythonisch". In Python 2.7 können Sie sogar schreiben True=Falseund (2+2==4)==Truekehren dann zurück False.
osa
8
Wie andere Antworten vermuten lassen, gibt es keine Möglichkeit oder Notwendigkeit, Konstanten zu deklarieren. Sie können diesen PEP jedoch über Konventionen lesen . zB THIS_IS_A_CONSTANT
Rasika Perera
34
@osa: Das kannst du in Python 3 nicht machen - SyntaxError: can't assign to keyword. Dies scheint eine gute Sache zu sein.
naught101
3
Überrascht wurde dies bisher noch nicht erwähnt, aber Enums scheinen eine gute Möglichkeit zu sein, aufgezählte Konstanten zu definieren.
CS95

Antworten:

973

Nein, da ist kein. Sie können eine Variable oder einen Wert in Python nicht als Konstante deklarieren. Ändere es einfach nicht.

Wenn Sie in einer Klasse sind, wäre das Äquivalent:

class Foo(object):
    CONST_NAME = "Name"

wenn nicht, ist es einfach

CONST_NAME = "Name"

Vielleicht möchten Sie sich aber das Code-Snippet Constants in Python von Alex Martelli ansehen .


Ab Python 3.8 gibt es eine typing.FinalVariablenanmerkung, die statischen Typprüfern (wie mypy) mitteilt, dass Ihre Variable nicht neu zugewiesen werden sollte. Dies ist das nächste Äquivalent zu Java final. Eine Neuzuweisung wird jedoch nicht verhindert :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
Felix Kling
quelle
27
Anstatt das zu tun, was in "Konstanten in Python" steht, sollten Sie die Funktion "Eigenschaft" oder den Dekorator verwenden.
Seth Johnson
26
Die Leute fragen nach der gleichen Funktion in Perl. Es gibt ein Importmodul namens "Use Constant", aber (AFAIK) ist nur ein Wrapper, um eine winzige Funktion zu erstellen, die den Wert zurückgibt. Ich mache das gleiche in Python. Beispiel:def MY_CONST_VALUE(): return 123
Kevinarpe
8
"Nein, da ist kein." Es stimmt, aber ich baue auf der Arbeit anderer Leute auf und habe weit unten eine Antwort mit einer kurzen und einfachen Implementierung von "Constants" für Python 2.7 (dem "enum" fehlt) hinzugefügt. Diese sind enum-like schreibgeschützt name.attributeund können einen beliebigen Wert enthalten. Die Deklaration ist einfach Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), die Verwendung ist unkompliziert print 10 + Nums.PI. Versuchen Sie, die Ergebnisse in Ausnahme Nums.PI = 22=> ValueError (..) zu ändern .
ToolmakerSteve
108
Ändere es einfach nicht. Du hast meinen Tag gemacht
Hi-Angel
89
"Ändere es einfach nicht" ist überhaupt nicht hilfreich. Es beantwortet die Frage nicht und ich würde vorschlagen, dass es entfernt wird.
Bartek Banachewicz
354

Es gibt kein constSchlüsselwort wie in anderen Sprachen, es ist jedoch möglich, eine Eigenschaft zu erstellen, die eine "Getter-Funktion" zum Lesen der Daten hat, aber keine "Setter-Funktion" zum erneuten Schreiben der Daten. Dies schützt die Kennung im Wesentlichen vor Änderungen.

Hier ist eine alternative Implementierung unter Verwendung der Klasseneigenschaft:

Beachten Sie, dass der Code für einen Leser, der sich über Konstanten wundert, alles andere als einfach ist. Siehe Erklärung unten

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

Code Erläuterung:

  1. Definieren Sie eine Funktion constant, die einen Ausdruck nimmt und daraus einen "Getter" erstellt - eine Funktion, die ausschließlich den Wert des Ausdrucks zurückgibt.
  2. Die Setter-Funktion löst einen TypeError aus, sodass er schreibgeschützt ist
  3. Verwenden Sie die constantsoeben erstellte Funktion als Dekoration, um schnell schreibgeschützte Eigenschaften zu definieren.

Und auf eine andere altmodischere Weise:

(Der Code ist ziemlich knifflig, weitere Erklärungen unten)

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

Beachten Sie, dass der @ applly-Dekorator veraltet zu sein scheint.

  1. Um den Bezeichner FOO zu definieren, definieren firs zwei Funktionen (fset, fget - die Namen sind nach meiner Wahl).
  2. Verwenden Sie dann die integrierte propertyFunktion, um ein Objekt zu erstellen, das "gesetzt" oder "get" werden kann.
  3. Beachten Sie, dass die propertyersten beiden Parameter der Funktion mit fsetund benannt sind fget.
  4. Verwenden Sie die Tatsache, dass wir genau diese Namen für unseren eigenen Getter & Setter ausgewählt haben, und erstellen Sie ein Schlüsselwortwörterbuch mit dem ** (Doppelsternchen), das auf alle lokalen Definitionen dieses Bereichs angewendet wird, um Parameter an die propertyFunktion zu übergeben
inv
quelle
11
Basierend auf der Dokumentation zu AttributeErrorund TypeErrordenke ich, dass die ausgelöste Ausnahme ein neuer Fehler sein sollte, den ich benenne, ConstantErroroder so ähnlich, der eine Unterklasse von ist TypeError. Der Abschnitt in den Dokumenten, der mich denken lässt, dass: docs.python.org/2/library/exceptions.html
ArtOfWarfare
3
Ich bin mit diesem Code überrascht. Warum haben die Methoden FOO () und BAR () die Schriftart self als Argument? Meine IDE unterstreicht die Klammern in Rot (Fehler "Kompilieren"). Ich war müde, mich darauf einzulassen, aber dann bekomme ich einen Fehler.
user3770060
10
Wenn Sie zu diesen Längen gehen, wird ein deutlicher Mangel in der Python-Sprache festgestellt. Warum hatten sie nicht das Bedürfnis, dies zu Python 3 hinzuzufügen? Ich kann nicht glauben, dass niemand es vorgeschlagen hat, und ich kann einfach nicht die Logik hinter einem Komitee sehen, das nah geht, Konstanten? nee. '
Andrew S
8
Und Ihre Lösung kann immer noch von einem entschlossenen Python-Programmierer mitCONST.__dict__['FOO'] = 7
pppery
11
@OscarSmith, ich denke, es würde das Design des selbstdokumentierten Codes verbessern. Wenn ich im Code deutlich mache, dass sich ein Wert nicht ändern kann, ist es einfacher zu verstehen, als den gesamten Quellcode zu lesen und zu erkennen, dass sich ein Wert nie ändert. Außerdem wird die Möglichkeit blockiert, dass jemand einen Wert ändert, der konstant sein sollte. Denken Sie daran: explizit ist besser als implizit.
Gabriel
112

In Python verwenden Benutzer anstelle von etwas, das die Sprache erzwingt, Namenskonventionen, z. B. __methodfür private Methoden und _methodfür geschützte Methoden.

Auf die gleiche Weise können Sie die Konstante einfach als alle Kappen deklarieren, z

MY_CONSTANT = "one"

Wenn Sie möchten, dass sich diese Konstante nie ändert, können Sie sich in den Attributzugriff einbinden und Tricks ausführen. Ein einfacherer Ansatz besteht jedoch darin, eine Funktion zu deklarieren

def MY_CONSTANT():
    return "one"

Das einzige Problem ist, dass Sie überall MY_CONSTANT () ausführen müssen, aber auch dies MY_CONSTANT = "one"ist (normalerweise) der richtige Weg in Python.

Sie können namedtuple auch verwenden , um Konstanten zu erstellen:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Anurag Uniyal
quelle
18
Wenn Sie dies tun, def MY_CONSTANT(): return "one"wird jemand später im Code nicht daran gehindert, etwas zu tun MY_CONSTANT = "two"(oder die Funktion neu zu deklarieren).
Matthew Schinckel
6
@MatthewSchinckel es geht um Konvention, auch das Ändern von MY_CONSTANT ändert nicht die Verwendung von MY_CONSTANT (), sondern wirft einen Fehler aus, und in Python kann Sie kein cleverer Trick schützen, wenn Sie möchten, dass Sie etwas ändern können.
Anurag Uniyal
3
Vielen Dank, dass Sie den Namedtuple-Ansatz angesprochen haben. Auf jeden Fall innovativ. Vielleicht finden Sie hier auch meinen "Kommentar" relevant.
RayLuo
@MatthewSchinckel Sie können ALLES in Python neu definieren, das ist also kein wirklich guter Punkt.
cslotty
@MatthewSchinckel Die Idee ist nicht zu schreiben MY_CONSTANT = MY_CONSTANT(), sondern MY_CONSTANT()als Konstante zu verwenden. Natürlich das. Dies ist jedoch in Ordnung und entspricht weitgehend dem Python-Prinzip "Wir sind alle Erwachsene hier" - dh es wird dem Entwickler selten verboten , eine Regel zu überschreiben, wenn er gute Gründe hat und weiß, was er tut.
jonathan.scholbach
69

Ich habe kürzlich ein sehr kurzes Update gefunden, das automatisch aussagekräftige Fehlermeldungen auslöst und den Zugriff über Folgendes verhindert __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'

Wir definieren uns selbst als Instanz und verwenden dann Slots, um sicherzustellen, dass keine zusätzlichen Attribute hinzugefügt werden können. Dadurch wird auch der __dict__Zugangsweg entfernt . Natürlich kann das gesamte Objekt immer noch neu definiert werden.

Bearbeiten - Ursprüngliche Lösung

Ich vermisse hier wahrscheinlich einen Trick, aber das scheint für mich zu funktionieren:

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

Durch das Erstellen der Instanz kann die magische __setattr__Methode Versuche zum Festlegen der FOOVariablen starten und abfangen . Sie könnten hier eine Ausnahme auslösen, wenn Sie möchten. Das Instanziieren der Instanz über den Klassennamen verhindert den direkten Zugriff über die Klasse.

Es ist ein totaler Schmerz für einen Wert, aber Sie könnten viel an Ihr CONSTObjekt anhängen . Mit einer Oberschicht scheint der Klassenname auch ein bisschen grottig zu sein, aber ich denke, dass er insgesamt ziemlich prägnant ist.

Jon Betts
quelle
11
Dies ist die beste und klarste Antwort, da sie den geringsten "Mechanismus", aber die meisten Funktionen aufweist. Das Auslösen einer Ausnahme ist jedoch wichtig ... keine Option.
Erik Aronesty
Ich habe eine kürzere Route ausgearbeitet, die automatisch bedeutungsvolle Fehler erzeugt, aber weitgehend dem gleichen Stil entspricht. Ich habe die ursprüngliche Idee hier zum Vergleich gelassen.
Jon Betts
Schade, dass Sie dieses CONST.Präfix noch brauchen . Auch in Situationen mit mehreren Modulen wird dies kompliziert.
Alfe
1
Ich denke, normalerweise möchten Sie Konstanten in dieser Situation sowieso in verwandte Bündel gruppieren (anstatt ein riesiges CONST-Objekt zu haben), also ist es wahrscheinlich keine so schlechte Sache.
Jon Betts
Warum ist diese Antwort immer noch so weit unten?! Die __slots__Lösung ist so elegant und effektiv. Nach allem, was ich gelesen habe, ist dies ungefähr so ​​nah wie das Erstellen von Konstanten in Python. Vielen Dank. Und für alle Interessierten gibt es hier eine brillante und ausführliche Erklärung der __slots__Magie.
JohnGalt
34

Python hat keine Konstanten.

Die vielleicht einfachste Alternative besteht darin, eine Funktion dafür zu definieren:

def MY_CONSTANT():
    return 42

MY_CONSTANT() hat jetzt alle Funktionen einer Konstanten (plus einige nervige Klammern).

Saeed Baig
quelle
1
Ich wollte nur diesen Vorschlag hinzufügen, aber zum Glück habe ich zu den Antworten mit niedriger Bewertung gescrollt. Ich hoffe, dass es weiter verbessert wird und ich stimme voll und ganz zu, dass es alle Funktionen einer Konstanten hat und sehr einfach und unkompliziert ist. Wenn ich mir die Menge an Boilerplate-Code in all den ausgeklügelten Lösungen anschaue, finde ich die Klammern relativ nervig.
Yaccob
1
Dies ist die einfachste Antwort, obwohl zu beachten ist, dass sie einen gewissen Overhead hat und Idioten nicht davon abhält, den Rückgabewert zu ändern. Es wird nur verhindern, dass der Code weiter unten die Quelle ändert
MrMesees
@ MrMesees Ändern des Rückgabewerts? Meinen Sie damit, die Quelle zu bearbeiten? Aber auch in C ++, wo Konstanten (wie constexpr) echte harte Konstanten sind, sind Sie nicht davor geschützt .
Ruslan
@Ruslan Ich meinte damit, dass Python, da es keinen Constexpr hat, die Bearbeitung des Werts nicht stoppen würde, nachdem es in einen äußeren Kontext zurückgegeben wurde. In diesem Beispiel wurde nichts unternommen, um den eingefrorenen Zustand zu erzwingen.
MrMesees
20

Zusätzlich zu den beiden wichtigsten Antworten (verwenden Sie einfach Variablen mit UPPERCASE-Namen oder verwenden Sie Eigenschaften, um die Werte schreibgeschützt zu machen) möchte ich erwähnen, dass es möglich ist, Metaklassen zu verwenden, um benannte Konstanten zu implementieren . Ich biete eine sehr einfache Lösung mit Metaklassen bei GitHub an, die hilfreich sein kann, wenn die Werte informativer über ihren Typ / Namen sein sollen:

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

Dies ist etwas fortgeschritteneres Python, aber immer noch sehr einfach zu bedienen und praktisch. (Das Modul verfügt über einige weitere Funktionen, einschließlich schreibgeschützter Konstanten, siehe README-Datei.)

Es gibt ähnliche Lösungen, die in verschiedenen Repositories im Umlauf sind, aber meines Wissens fehlt ihnen entweder eines der grundlegenden Merkmale, die ich von Konstanten erwarten würde (wie Konstante oder beliebiger Typ), oder es wurden esoterische Merkmale hinzugefügt machen sie weniger allgemein anwendbar. Aber YMMV, ich wäre dankbar für Feedback. :-)

hans_meine
quelle
3
Ich mag deine Implementierung auf GitHub. Ich war fast bereit, eine Basisklasse zu schreiben, die die Reverse-Lookup-Funktionalität implementierte, aber ich sehe, dass Sie das und mehr getan haben!
Kerr
Danke, @Kerr, es ist das erste Feedback, das ich bekommen habe und das mich glücklich gemacht hat. :-)
hans_meine
Genial. Ich habe das gerade ausprobiert. Schön, dies als Option zu haben. Obwohl ich mich noch nicht entschieden habe, ob mir der schreibgeschützte Aspekt wichtig genug ist, um ihn zu verwenden, anstatt ihn einfach zu tun def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), gemäß stackoverflow.com/a/1695250/199364 , Abschnitt "In früheren Versionen ..."
ToolmakerSteve
19

Eigenschaften sind eine Möglichkeit, Konstanten zu erstellen. Sie können dies tun, indem Sie eine Getter-Eigenschaft deklarieren, den Setter jedoch ignorieren. Zum Beispiel:

class MyFinalProperty(object):

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

In einem Artikel, den ich geschrieben habe , finden Sie weitere Möglichkeiten zur Verwendung von Python-Eigenschaften.

nvd
quelle
Unterbewertete Lösung. Ich habe dies gerade implementiert, nachdem ich diese Seite gefunden hatte (nicht diese Antwort) und bin zurückgekreist, um sie hinzuzufügen, wenn nicht bereits. Ich wollte die Nützlichkeit dieser Antwort unterstreichen.
Marc
18

Bearbeiten: Beispielcode für Python 3 hinzugefügt

Hinweis: Diese andere Antwort scheint eine viel vollständigere Implementierung zu bieten, die der folgenden ähnelt (mit mehr Funktionen).

Erstellen Sie zunächst eine Metaklasse :

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

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

Dadurch wird verhindert, dass statische Eigenschaften geändert werden. Erstellen Sie dann eine andere Klasse, die diese Metaklasse verwendet:

class Const(object):
    __metaclass__ = MetaConst

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

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

Oder wenn Sie Python 3 verwenden:

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

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

Dies sollte verhindern, dass Instanz-Requisiten geändert werden. Um es zu verwenden, erben Sie:

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

Jetzt sollten die Requisiten, auf die direkt oder über eine Instanz zugegriffen wird, konstant sein:

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

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

Hier ist ein Beispiel von oben in Aktion. Hier ist ein weiteres Beispiel für Python 3.

Doppelwirbel
quelle
10

Sie können ein namedtuple als Problemumgehung verwenden, um effektiv eine Konstante zu erstellen, die genauso funktioniert wie eine statische endgültige Variable in Java (eine Java- "Konstante"). Umgehungen sind irgendwie elegant. (Ein eleganterer Ansatz wäre, einfach die Python-Sprache zu verbessern - welche Art von Sprache können Sie neu definieren math.pi? - aber ich schweife ab.)

(Während ich dies schreibe, stelle ich eine andere Antwort auf diese Frage fest, die als namedtuple bezeichnet wird, aber ich werde hier fortfahren, da ich eine Syntax zeige, die den in Java erwarteten Erwartungen besser entspricht, da kein Name erstellt werden muss geben Sie als namedtuple zwingt Sie zu tun.)

Wenn Sie Ihrem Beispiel folgen, werden Sie sich daran erinnern, dass wir in Java die Konstante innerhalb einer Klasse definieren müssen . Da Sie keinen Klassennamen erwähnt haben, nennen wir ihn Foo. Hier ist die Java-Klasse:

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

Hier ist das entsprechende Python.

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

Der wichtigste Punkt, den ich hier hinzufügen möchte, ist, dass Sie keinen separaten FooTyp benötigen (ein "anonymes benanntes Tupel" wäre nett, obwohl das wie ein Oxymoron klingt), also benennen wir unser benanntes Tupel, damit es _Foohoffentlich nicht funktioniert Escape zum Importieren von Modulen.

Der zweite Punkt hier ist, dass wir sofort eine Instanz des Nametupels erstellen und es aufrufen Foo; Es ist nicht erforderlich, dies in einem separaten Schritt zu tun (es sei denn, Sie möchten). Jetzt können Sie das tun, was Sie in Java tun können:

>>> Foo.CONST_NAME
'Name'

Aber Sie können es nicht zuordnen:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Bestätigung: Ich dachte, ich hätte den Namedtuple-Ansatz erfunden, aber dann sehe ich, dass jemand anderes eine ähnliche (wenn auch weniger kompakte) Antwort gegeben hat. Dann bemerkte ich auch Was sind "benannte Tupel" in Python? , was darauf hinweist, dass sys.version_infoes sich nun um ein Namedtupel handelt, sodass die Python-Standardbibliothek diese Idee vielleicht schon viel früher hatte.

Beachten Sie, dass Sie leider (dies ist immer noch Python) die gesamte FooZuweisung insgesamt löschen können :

>>> Foo = 'bar'

(Gesichtspalme)

Aber zumindest verhindern wir, dass sich der Foo.CONST_NAMEWert ändert, und das ist besser als nichts. Viel Glück.

Garret Wilson
quelle
Vielen Dank, dass Sie den Namedtuple-Ansatz angesprochen haben. Auf jeden Fall innovativ. Vielleicht finden Sie hier auch meinen "Kommentar" relevant.
RayLuo
10

PEP 591 hat das "letzte" Qualifikationsmerkmal. Die Durchsetzung liegt bei der Typprüfung.

So können Sie tun:

MY_CONSTANT: Final = 12407

Hinweis: Das Final Schlüsselwort gilt nur für die Python 3.8-Version

Mike Amy
quelle
9

Hier ist eine Implementierung einer "Constants" -Klasse, die Instanzen mit schreibgeschützten (konstanten) Attributen erstellt. ZB kann verwendet werden Nums.PI, um einen Wert abzurufen, der als initialisiert wurde 3.14159, und Nums.PI = 22löst eine Ausnahme aus.

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

Dank @MikeGrahams FrozenDict , das ich als Ausgangspunkt verwendet habe. Geändert, also anstelle Nums['ONE']der Verwendungssyntax Nums.ONE.

Und dank der Antwort von @ Raufio für die Idee, __ setattr __ zu überschreiben.

Eine Implementierung mit mehr Funktionen finden Sie unter @Hans_meine named_constants bei GitHub

ToolmakerSteve
quelle
2
Python ist eine Sprache, in der Erwachsene zustimmen. Es gibt keinen Schutz gegen so etwas. Nums._d['PI'] = 22 Ich glaube, die Sprache selbst bietet keine Möglichkeit, Dinge als nicht veränderlich zu kennzeichnen.
Ajay M
8

Ein Tupel qualifiziert sich technisch als Konstante, da ein Tupel einen Fehler auslöst, wenn Sie versuchen, einen seiner Werte zu ändern. Wenn Sie ein Tupel mit einem Wert deklarieren möchten, setzen Sie ein Komma nach dem einzigen Wert wie folgt:

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

Verwenden Sie Folgendes, um den Wert dieser Variablen zu überprüfen:

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

Wenn Sie versuchen, diesen Wert zu ändern, wird ein Fehler ausgegeben.

tobahhh
quelle
7

Ich würde eine Klasse erstellen, die die __setattr__Methode der Basisobjektklasse überschreibt, und meine Konstanten damit umschließen. Beachten Sie, dass ich Python 2.7 verwende:

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)

So wickeln Sie eine Zeichenfolge ein:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

Es ist ziemlich einfach, aber wenn Sie Ihre Konstanten genauso verwenden möchten wie ein nicht konstantes Objekt (ohne constObj.value zu verwenden), ist es etwas intensiver. Es ist möglich, dass dies zu Problemen führen kann. .valueDaher ist es möglicherweise am besten, die Anzeige beizubehalten und zu wissen, dass Sie Operationen mit Konstanten ausführen (möglicherweise jedoch nicht die "pythonischste" Methode).

Raufio
quelle
+1 für interessanten Ansatz. Obwohl nicht so sauber wie die Antworten, die bereits gegeben wurden. Und selbst die einfachste früher vorgeschlagene Lösung def ONE(): return 1ist einfacher zu verwenden ONE()als diese Antwort ONE.value.
ToolmakerSteve
7

Leider hat der Python noch keine Konstanten und es ist eine Schande. ES6 hat JavaScript bereits Unterstützungskonstanten hinzugefügt ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ), da dies in jeder Programmiersprache sehr nützlich ist. Wie in anderen Antworten in der Python-Community beantwortet, verwenden Sie die Konvention - Benutzer-Großbuchstabenvariable als Konstanten, sie schützt jedoch nicht vor willkürlichen Fehlern im Code. Wenn Sie möchten, kann es sein, dass Sie als Nächstes eine Einzeldateilösung als nützlich erachten (siehe Dokumentation zu deren Verwendung).

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

Wenn dies nicht ausreicht, lesen Sie den vollständigen Testfall.

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

Vorteile: 1. Zugriff auf alle Konstanten für das gesamte Projekt 2. Strikte Kontrolle der Werte von Konstanten

Fehlt: 1. Keine Unterstützung für benutzerdefinierte Typen und den Typ 'Dikt'

Anmerkungen:

  1. Getestet mit Python3.4 und Python3.5 (ich benutze das 'Tox' dafür)

  2. Testumgebung:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
PADYMKO
quelle
Sie können dies leicht verbessern, indem Sie Wörterbücher automatisch in benannte Tupel konvertieren
Peter Schorn
6

Die pythonische Art, "Konstanten" zu deklarieren, ist im Grunde eine Variable auf Modulebene:

RED = 1
GREEN = 2
BLUE = 3

Und dann schreiben Sie Ihre Klassen oder Funktionen. Da Konstanten fast immer ganze Zahlen sind und auch in Python unveränderlich sind, haben Sie nur eine sehr geringe Chance, sie zu ändern.

Es sei denn natürlich, wenn Sie explizit festlegen RED = 2.

Xavier Ho
quelle
21
Ja aber Blockieren der Fähigkeit zum "expliziten Setzen RED = 2" ist der gesamte Vorteil (in anderen Sprachen), einen Variablennamen als "konstant" deklarieren zu können!
ToolmakerSteve
6
Würden Sie davon profitieren, wenn Sie das blockieren? Das Nützlichste an const sind normalerweise Compiler-Optimierungen, was in Python eigentlich nicht der Fall ist. Willst du, dass etwas konstant ist? Ändere es einfach nicht. Wenn Sie sich Sorgen machen, dass jemand anderes es ändert, können Sie es einfach außerhalb seines Anwendungsbereichs platzieren oder einfach erkennen, dass, wenn jemand es ändert, dies sein Problem ist und er sich damit befassen muss, nicht Sie.
Kevin
@ Kevin: " Würde davon profitieren, würdest du ... ", der Vorteil vonstatic , einen einzigen Speicher für den Wert für alle Instanzen einer Klasse zu haben? Es sei denn, es besteht tatsächlich die Möglichkeit, eine statische / Klassenvariable zu deklarieren.
Minuten
8
Das Hauptproblem ist, dass einige es als einen Wert betrachten, der eine Quelle der Wahrheit ist, der nicht geändert werden kann, und ihn als Quelle der Wahrheit in ihrem gesamten Code verwenden, anstatt magische Werte einzuführen (von denen ich in Python viel sehe). - und andere sehen es vielleicht als etwas, das sie nach Belieben ändern dürfen. Wenn jemand eine globale Variable ändert und Sie nicht sagen können, wo sie geändert wurde, und die Anwendung abstürzt, weil ROT = "blau" statt "rot", führen Sie ein völlig unnötiges Problem ein, das bereits so einfach und gelöst wurde wird allgemein verstanden.
Dagrooms
5

Wir können ein Deskriptorobjekt erstellen.

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) Wenn wir mit Konstanten auf Instanzebene arbeiten wollten, dann:

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) Wenn wir Konstanten nur auf Klassenebene erstellen möchten, können wir eine Metaklasse verwenden, die als Container für unsere Konstanten (unsere Deskriptorobjekte) dient. Alle Klassen, die absteigen, erben unsere Konstanten (unsere Deskriptorobjekte) ohne das Risiko, dass sie geändert werden können.

# 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

Wenn ich eine Unterklasse von Foo erstelle, erbt diese Klasse die Konstante, ohne dass die Möglichkeit besteht, sie zu ändern

class Bar(Foo): pass

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

Python-Wörterbücher sind veränderbar, daher scheinen sie keine gute Möglichkeit zu sein, Konstanten zu deklarieren:

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

Hier ist ein Trick, wenn Sie Konstanten wollen und sich nicht um ihre Werte kümmern:

Definieren Sie einfach leere Klassen.

z.B:

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

In Python ist eine Konstante einfach eine Variable mit einem Namen in allen Großbuchstaben, wobei die Wörter durch den Unterstrich getrennt sind.

z.B

DAYS_IN_WEEK = 7

Der Wert ist veränderbar, da Sie ihn ändern können. Aber angesichts der Regeln für den Namen, die Ihnen sagen, dass es sich um eine Konstante handelt, warum sollten Sie das tun? Ich meine, es ist doch dein Programm!

Dies ist der Ansatz, der in Python verfolgt wird. Aus privatedemselben Grund gibt es kein Schlüsselwort. Stellen Sie dem Namen einen Unterstrich voran, und Sie wissen, dass er privat sein soll. Code kann gegen die Regel verstoßen ... genau wie ein Programmierer das private Schlüsselwort sowieso entfernen könnte.

Python hätte ein constSchlüsselwort hinzufügen können ... aber ein Programmierer könnte das Schlüsselwort entfernen und dann die Konstante ändern, wenn er möchte, aber warum? Wenn Sie die Regel brechen möchten, können Sie die Regel trotzdem ändern. Aber warum sollte man sich die Mühe machen, die Regel zu brechen, wenn der Name die Absicht klar macht?

Vielleicht gibt es einen Unit-Test, bei dem es sinnvoll ist, eine Wertänderung vorzunehmen? Um zu sehen, was für eine 8-Tage-Woche passiert, obwohl in der realen Welt die Anzahl der Tage in der Woche nicht geändert werden kann. Wenn die Sprache Sie daran gehindert hat, eine Ausnahme zu machen, wenn es nur diesen einen Fall gibt, müssen Sie die Regel brechen ... Sie müssten dann aufhören, sie als Konstante zu deklarieren, obwohl sie in der Anwendung immer noch eine Konstante ist, und es gibt sie Nur dieser eine Testfall zeigt, was passiert, wenn er geändert wird.

Der Name in Großbuchstaben gibt an, dass es sich um eine Konstante handeln soll. Das ist wichtig. Keine Sprache, die Einschränkungen für Code erzwingt, die Sie ohnehin ändern können.

Das ist die Philosophie von Python.

innov8
quelle
4

Es gibt keinen perfekten Weg, dies zu tun. So wie ich es verstehe, werden die meisten Programmierer nur den Bezeichner großschreiben, so dass PI = 3.142 leicht als Konstante verstanden werden kann.

Wenn Sie andererseits etwas wollen, das sich tatsächlich wie eine Konstante verhält, bin ich mir nicht sicher, ob Sie es finden werden. Bei allem, was Sie tun, gibt es immer eine Möglichkeit, die "Konstante" zu bearbeiten, sodass sie nicht wirklich eine Konstante ist. Hier ist ein sehr einfaches, schmutziges Beispiel:

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

Dies scheint eine Konstante im PHP-Stil zu sein.

In Wirklichkeit braucht jemand nur Folgendes, um den Wert zu ändern:

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

Dies gilt auch für alle anderen Lösungen, die Sie hier finden - selbst für die cleveren, die eine Klasse bilden und die Set-Attribut-Methode neu definieren -, es wird immer einen Weg geben, sie zu umgehen. So ist Python eben.

Meine Empfehlung ist, einfach den ganzen Ärger zu vermeiden und nur Ihre Identifikatoren groß zu schreiben. Es wäre nicht wirklich eine richtige Konstante, aber nichts würde es tun.

John
quelle
4

Es gibt einen saubereren Weg, dies mit namedtuple zu tun:

from collections import namedtuple


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

Anwendungsbeispiel

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

Mit genau diesem Ansatz können Sie Ihre Konstanten mit einem Namespace versehen.

Juan Ignacio Sánchez
quelle
Beachten Sie für alle, die dies lesen, bitte, dass jeder seinen internen Wert ändern kann, wenn Sie ein veränderliches Objekt als eine dieser Konstanten festlegen. Wenn Sie beispielsweise bar = [1, 2, 3] verwenden, können Sie wie folgt vorgehen: CONSTS.bar [1] = 'a' und es wird nicht abgelehnt. Also sei vorsichtig damit.
Juan Ignacio Sánchez
Anstelle dieser hackigen Methode, die ich nur zum Spaß gemacht habe, empfehle ich, stattdessen Pythons Eigenschaftsdekorator zu verwenden.
Juan Ignacio Sánchez
4

Vielleicht hilft dir die pconst Bibliothek ( github ).

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.

sim
quelle
3

Sie können StringVar oder IntVar usw. verwenden. Ihre Konstante ist const_val

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

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

Sie können es tun mit collections.namedtupleund itertools:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Jaunty Sankey
quelle
2

(Dieser Absatz sollte einen Kommentar zu den hier und da erwähnten Antworten sein , die erwähnt wurden namedtuple, aber es wird zu lang, um in einen Kommentar zu passen, also geht es los.)

Der oben erwähnte Namedtuple-Ansatz ist definitiv innovativ. Der Vollständigkeit halber heißt es jedoch am Ende des NamedTuple-Abschnitts seiner offiziellen Dokumentation :

Aufzählungskonstanten können mit benannten Tupeln implementiert werden, aber es ist einfacher und effizienter, eine einfache Klassendeklaration zu verwenden:

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

Mit anderen Worten, die offizielle Dokumentation bevorzugt eine praktische Methode, anstatt das schreibgeschützte Verhalten tatsächlich zu implementieren. Ich denke, es wird ein weiteres Beispiel dafür Zen of Python :

Einfach ist besser als komplex.

Praktikabilität schlägt Reinheit.

RayLuo
quelle
2

Hier ist eine Sammlung von Redewendungen, die ich erstellt habe, um einige der bereits verfügbaren Antworten zu verbessern.

Ich weiß, dass die Verwendung von Konstanten nicht pythonisch ist, und Sie sollten dies nicht zu Hause tun!

Python ist jedoch eine so dynamische Sprache! Dieses Forum zeigt, wie es möglich ist, Konstrukte zu erstellen, die wie Konstanten aussehen und sich anfühlen. Diese Antwort hat in erster Linie den Zweck zu untersuchen, was durch die Sprache ausgedrückt werden kann.

Bitte sei nicht zu hart mit mir :-).

Für weitere Details habe ich einen Begleitblog über diese Redewendungen geschrieben .

In diesem Beitrag werde ich eine konstante Variable auf eine konstante Referenz auf Werte (unveränderlich oder anderweitig) aufrufen. Außerdem sage ich, dass eine Variable einen eingefrorenen Wert hat, wenn sie auf ein veränderliches Objekt verweist, dessen Wert ein Client-Code nicht aktualisieren kann.

Ein Raum von Konstanten (SpaceConstants)

Diese Redewendung erstellt einen Namespace konstanter Variablen (auch bekannt als SpaceConstants). Es ist eine Modifikation eines Code-Snippets von Alex Martelli , um die Verwendung von Modulobjekten zu vermeiden. Diese Modifikation verwendet insbesondere eine sogenannte Klassenfactory, da innerhalb der SpaceConstants- Funktion eine Klasse namens SpaceConstants verwendet wird definiert und eine Instanz davon zurückgegeben wird.

Ich habe die Verwendung von Class Factory untersucht, um ein richtlinienbasiertes Design in Python im Stackoverflow und auch in einem Blogpost zu implementieren .

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"

Ein Leerzeichen mit eingefrorenen Werten (SpaceFrozenValues)

Diese nächste Redewendung ist eine Modifikation der SpaceConstants, in denen referenzierbare veränderbare Objekte eingefroren sind. Diese Implementierung nutzt das, was ich als Shared Closure zwischen setattr- und getattr- Funktionen bezeichne. Der Wert des veränderlichen Objekts wird kopiert und von der Variablen- Cache- Definition innerhalb des gemeinsam genutzten Funktionsabschlusses referenziert . Es bildet eine so genannte schließungsgeschützte Kopie eines veränderlichen Objekts .

Sie müssen bei der Verwendung dieser Redewendung vorsichtig sein, da getattr den Wert des Caches durch eine tiefe Kopie zurückgibt . Dieser Vorgang kann erhebliche Auswirkungen auf die Leistung großer Objekte haben!

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"

Ein konstanter Raum (ConstantSpace)

Diese Redewendung ist ein unveränderlicher Namespace von konstanten Variablen oder ConstantSpace . Es ist eine Kombination aus einer unglaublich einfachen Antwort von Jon Betts im Stackoverflow mit einer Klassenfabrik .

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

Ein gefrorener Raum (FrozenSpace)

Diese Redewendung ist ein unveränderlicher Namespace von eingefrorenen Variablen oder FrozenSpace . Es wird aus dem vorherigen Muster abgeleitet, indem jede Variable durch Schließen der generierten FrozenSpace- Klasse zu einer geschützten Eigenschaft gemacht wird .

from copy import deepcopy

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

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

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

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

In Python sind Konstanten nicht vorhanden. Sie können jedoch angeben, dass eine Variable eine Konstante ist und nicht geändert werden darf, indem Sie CONST_am Anfang des Variablennamens hinzufügen und in einem Kommentar angeben, dass es sich um eine Konstante handelt:

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.

Alternativ können Sie eine Funktion erstellen, die sich wie eine Konstante verhält:

def CONST_daysInWeek():
    return 7;

quelle
1

In meinem Fall benötigte ich unveränderliche Bytearrays für die Implementierung einer Kryptobibliothek mit vielen Literalzahlen, die ich sicherstellen wollte, dass sie konstant sind.

Diese Antwort funktioniert, aber die versuchte Neuzuweisung von Bytearray-Elementen führt nicht zu einem Fehler.

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

Konstanten sind unveränderlich, aber die konstante Bytearray-Zuweisung schlägt lautlos fehl:

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

Ein leistungsfähigerer, einfacher und vielleicht sogar "pythonischer" Ansatz beinhaltet die Verwendung von Memoryview-Objekten (Pufferobjekte in <= 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)

Die Zuordnung von ConstArray-Elementen ist TypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222
jxqz
quelle
1

Ich schreibe eine util lib für python const: kkconst - pypi support str, int, float, datetime

Die const-Feldinstanz behält ihr Basistypverhalten bei.

Zum Beispiel:

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

Weitere Details Verwendung Sie können die Pypi-URL lesen: Pypi oder Github

kaka_ace
quelle
1

Sie können eine Konstante in ein Numpy-Array einschließen, sie nur als Schreibzugriff kennzeichnen und sie immer mit dem Index Null aufrufen.

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

Dies schützt natürlich nur den Inhalt des Numpys, nicht die Variable "CONSTANT" selbst; Sie können immer noch tun:

CONSTANT = 'foo'

und CONSTANTwürde sich ändern, aber das würde schnell einen TypeError auslösen, wenn das erste Mal CONSTANT[0]später im Skript aufgerufen wird.

obwohl ... ich nehme an, wenn du es irgendwann geändert hast

CONSTANT = [1,2,3]

Jetzt würden Sie den TypeError nicht mehr bekommen. hmmmm ....

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

Präsenz
quelle