Keine Konstanten in Python: warum?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
fredistdurstig
User
Beiträge: 13
Registriert: Sonntag 18. Januar 2009, 20:08

Hallo!
Ich programmieren seit einigen Wochen Scala. Ein besonderes Merkmal von Scala ist die Verwendung von Konstanten, wo immer es auch geht (was auch in Java zu einem guten Stil gehoert).
Nun habe ich gerade mitbekommen, dass es in Python gar keine "echten" Konstanten gibt, was mich sehr wundert.
Mich wuerde sehr interessieren, warum Herr van Rossum diese Entscheidung getroffen hat - er muss ja bestimmte Gruende dafuer gehabt haben? Weiss jemand, welche Gruende das waren, bzw. wo man das nachlesen kann?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Ich weiß nicht, was GvR sich dabei gedacht hat, aber ich denke mir dabei, dass es schlicht überflüssig gewesen wäre. Wozu explizit Konstanten?
fredistdurstig
User
Beiträge: 13
Registriert: Sonntag 18. Januar 2009, 20:08

- Allgemein passieren oft Fehler, weil man eine Variable versehentlich neu zuweist -> Leichtere Fehlersuche: ein falscher Wert kann dann nur vom Deklarationsort her kommen
- Förderung von funktionalem Programmierstil -> Vermeidung von Seiteneffekten

Okay, bei Ruby gibt es auch keine echten Konstanten. So wie ich das sehe, sind Konstanten wohl ähnlich zu betrachten wie statische Typisierung: wie die Typen müssten sie vom Interpreter überprüft werden.
Trotzdem leuchtet mir der Sinn, diese komplett wegzulassen, nicht ganz ein.

Gibts noch andere Argumente?
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Ich tippe mal drauf, dass das einfach ein zusätzlicher Implementierungsaufwand gewesen wäre.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

es gibt doch eine Konvention
pep8 hat geschrieben: Constants

Constants are usually declared on a module level and written in all
capital letters with underscores separating words. Examples include
MAX_OVERFLOW and TOTAL.
Also mir reicht die, und ich bin noch nie auf die Idee gekommen im produktiven Umfeld an einer solchen "Konstante" zu drehen.
Reicht das nicht?!
BlackJack

@fredistdurstig: Vielleicht fehlte Guido auch einfach ein Nachweis dafür, dass allgemein oft Fehler durch versehentliche Neuzuweisung passieren. Tun sie das denn? Quelle für die Behauptung?

Mir passiert das jedenfalls nicht so oft und sehr selten bis gar nicht wenn der Name komplett in Grossbuchstaben geschrieben ist. Das ist eine Konvention, die auch in vielen Sprachen mit "echten" Konstanten gilt, und wer `PI` oder `LIGHT_SPEED` *aus versehen* neu bindet, sollte die Finger vom Programmieren lassen -- egal in welcher Programmiersprache. ;-)

Bei Java möglichst weitreichend ``final`` an alles dranzuschreiben sehe ich nicht als Möglichkeit Fehler zu verhindern, sondern dem (JIT-)Compiler den Hinweis zu geben, dass er, wenn die Variable erst einmal zugewiesen ist, den Wert überall "inlinen" kann und so den Zugriff potentiell schneller machen kann.
fredistdurstig
User
Beiträge: 13
Registriert: Sonntag 18. Januar 2009, 20:08

Habe leide keiner Statistik über die Fehler-Rate bei der versehentlichen Neuzuweisung von Konstanten hier ;)

Okay, wie gesagt, wahrscheinlich könnte man jetzt auch anfangen, über den Sinn und Unsinn von dynamischer/statischer Typisierung zu diskutieren. Aber da bin ich hier im falschen Forum ;)
Bei Konstanten ist das wohl die gleiche Diskussion.
Wahrscheinlich sind echte Konstanten auch nur bei kompilierten Sprachen möglich. Ich hätte aber wenigstens ein in die Sprache eingebautes Konstrukt erwartet wie z.B. das eine Exception zur Laufzeit geworfen wird.
Bei Java möglichst weitreichend ``final`` an alles dranzuschreiben sehe ich nicht als Möglichkeit Fehler zu verhindern, sondern dem (JIT-)Compiler den Hinweis zu geben, dass er, wenn die Variable erst einmal zugewiesen ist, den Wert überall "inlinen" kann und so den Zugriff potentiell schneller machen kann.
Ich würde sagen, der Hauptgrund ist eher, das der Compiler zur Compilezeit schon überprüfen kann, ob die Konstante fälschlicherweise mehrfach zugewiesen wird (also wie bei statischer Typisierung). Aber okay, schneller ist es auch - stimmt.
Aber diese beiden Aspekte interessieren in dynamischen Sprachen ja niemanden ;)

In Scala ist die Verwendung von Konstanten und Variablen übrigens sehr schön gelöst:

Code: Alles auswählen

var i = 5      // Variable
val j = 5      // Konstante
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

fredistdurstig hat geschrieben:In Scala ist die Verwendung von Konstanten und Variablen übrigens sehr schön gelöst:

Code: Alles auswählen

var i = 5      // Variable
val j = 5      // Konstante
Und genau sowas soll Python bitte *nicht* einführen.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

fredistdurstig hat geschrieben:Aber okay, schneller ist es auch - stimmt.
Aber diese beiden Aspekte interessieren in dynamischen Sprachen ja niemanden ;)
Der zweite Aspekt ist schon interessant, siehe auch die Bemühungen von UnladenSwallow oder PyPy.
fredistdurstig hat geschrieben:In Scala ist die Verwendung von Konstanten und Variablen übrigens sehr schön gelöst:

Code: Alles auswählen

var i = 5      // Variable
val j = 5      // Konstante
Hmm, wenn schon würde ich const verwenden. val und var ist doch viel zu ähnlich!
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

fredistdurstig hat geschrieben:

Code: Alles auswählen

var i = 5      // Variable
val j = 5      // Konstante
Jeder Programmierer bekommt eingehämmert, dass das Verwenden von ziemlich ähnlichen Variablennamen *Pfui* ist. So etwas dann in die Syntax einer Sprache zu gießen ist schon bösartig.
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

Da muss man ja drei Mal hinsehen bis man den Unterschied zwischen val und var erkennt oO

Keine gute Idee.
philistion
User
Beiträge: 108
Registriert: Sonntag 7. Februar 2010, 14:16

Ich fände das Feature von Ruby, eine Warnung auszugeben wenn eine Konstante (alles was großgeschrieben ist) verändert wird, schon nett. Sollte van Rossum sich vielleicht auch mal durch den Kopf gehen lassen..
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

philistion hat geschrieben:Ich fände das Feature von Ruby, eine Warnung auszugeben wenn eine Konstante (alles was großgeschrieben ist) verändert wird, schon nett. Sollte van Rossum sich vielleicht auch mal durch den Kopf gehen lassen..
Gibt doch auch externe Tools, mit dem man sowas analysieren kann, das muss doch nicht extra in die Sprache. Z.B. pylint

Code: Alles auswählen

ID:W0621 LatexMaker.__init__: Redefining name 'MAX_RUNS' from outer scope (line 75)	latexmk.py	/latexmk.py	line 82	PyLint Problem
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und wenn man sowas unbedingt zur Laufzeit haben möchte:

Code: Alles auswählen

constants = {}

class Const(object):
    _instance = None

    def __new__(cls):
        if not cls._instance:
            cls._instance = object.__new__(cls)
        return cls._instance

    def __getattr__(self, name):
        try:
            return constants[name]
        except KeyError:
            raise AttributeError, 'Undefined constant'

    def __setattr__(self, name, value):
        if not name in constants:
            constants[name] = value
        else:
            raise AttributeError, 'Cannot redefine constant'

Code: Alles auswählen

>>> from const import Const
>>> const = Const()
>>> const.FOO
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "const.py", line 8, in __getattr__
    raise AttributeError, 'Undefined constant'
AttributeError: Undefined constant
>>> const.FOO = 'bar'
>>> const.FOO
'bar'
>>> const.FOO = 'baz'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "const.py", line 13, in __setattr__
    raise AttributeError, 'Cannot redefine constant'
AttributeError: Cannot redefine constant
Direkt auf Namespace-Ebene ist sowas meines Wissens nicht möglich. Daher der Notbehelf über ein Singleton mit den Konstanten als Attribute (in Wirklichkeit halt ein Dictionary). Ich habe das bewusst nicht an die Klasse gebunden, um Rekursionsprobleme beim Getter + Setter zu vermeiden.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@snafu Man kann dass Modul ersetzen und in __setattr__ eine Exception werfen wenn einem Namen der in Großbuchstaben geschrieben ist ein neues Objekt zugewiesen wird, hilft aber auch nur bedingt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ein paar Anmerkungen: Wie die meisten funktionalen Sprachen, sind bei Scala Variablen konzeptuell keine Zellen, in die man Dinge tut, sondern Namen für Teilausdrücke. Haben diese Ausdrücke keinen Seiteneffekt, kann man sogar noch nicht einmal erkennen, wann und wie oft sie berechnet werden. Dann sind es Konstanten. Ein schauer Compiler sollte sie nämlich nur 1x berechnen und dann statt der Variable ihren Wert und nicht den Teilausdruck einsetzen.

Ich persönlich finde es eigentlich recht elegant, dass bei Scala `val` und `var` so ähnlich aussehen. In den Code gesprenkelte `final`-Modifikatoren wie bei Java stören dagegen meine Ästhetik.

Python hat ein anderes (klassisches) Ausführungsmodell. Hier basiert alles auf Dictionaries und Veränderbarkeit ist der Normalfall (und nicht die Ausnahme wie bei funktionalen Sprachen). Konstanten wären denkbar, müssten aber zur Laufzeit vom Interpreter geprüft werden, bräuchten irgendwie Syntax und es war GvR offenbar nicht den Aufwand wert.

Letztlich ist eine Konstanten-Deklaration ja ein Hinweis für den Entwickler und damit in erster Linie Dokumentation (einer Annahme, die von einem Compiler überprüft werden kann). In Python muss man einfach auf die Prüfung durch den Compiler verzichten. Dafür gibt es (wie schon gesagt) andere Tools, die versuchen, ein paar statische Informationen aus Python Code zu extrahieren.

Ruby hat sehr wohl Konstanten. Variablen, die mit einem Großbuchstaben beginnen, gelten als konstant und es gibt eine Warnung des Interpreters, wenn man diesen ein zweites Mal etwas zuweist.

Übrigens, wer `final` in Java im Glauben benutzt, dem JIT-Compiler einen Gefallen zu tun, der kann das gleich lassen. Hotspot ist viel besser daran, konstante Teilausdrücke auch über mehrere Inline-Stufen zu finden und zu optimieren und Methoden zu de-virtualisieren, als das der Programmierer kann. Daher sollte `final` nur dann benutzt werden, wenn man dem Mitentwickler signalisieren will, dass eine Variable nicht als veränderbar gedacht ist, eine Methode oder Klasse nicht für Vererbung vorgesehen ist oder wenn inner classes es aus syntaktischen Gründen erfordern. Aber niemals, weil man glaubt, der Code würde effizienter.

Letztlich sind alle statischen Typdeklarationen (zu denen auch gehört, etwas als unveränderbar zu kennzeichnen) explizite Redundanzen, die helfen, Fehler zu finden, wenn man implizit im Code vorhandene Annahmen nicht einhält. Dumm nur, dass manchmal derartige statische Typdeklarationen den Code schwerer verständlich machen, als wenn sie fehlen würden. Und genau das ist dann das Argument gegen statische und für dynamische Typisierung.

Stefan
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Code: Alles auswählen

import __builtin__
from types import ModuleType

class ConstNameError(NameError):
    pass

class ConstNameModule(ModuleType):
    def __setattr__(self, name, value):
        if name.isupper() and hasattr(self, name):
            raise ConstNameError("Tried to reset const name")

        ModuleType.__setattr__(self, name, value)

_old_import = __import__
def const_name_import(*args, **kwargs):
    module = _old_import(*args, **kwargs)
    const_name_module = ConstNameModule(module.__name__)

    const_name_module.__dict__.clear()
    const_name_module.__dict__.update(module.__dict__)

    return const_name_module
__builtin__.__import__ = const_name_import
Funktioniert nur nach vollendetem Import. Näher käme man nur dann dran, wenn man imp.load_module() den eigenen Modul-Typ übergeben könnte, da die C Implementation von imp.load_module() in Objects/import.c PyModule_New() direkt aufruft, und man somit keine Möglichkeit hat, da irgendwas mit zb. dem types Module zu drehen. Oder man schreibt den kompletten Import-Mechanismus neu, was im Grunde gehen sollte.
philistion
User
Beiträge: 108
Registriert: Sonntag 7. Februar 2010, 14:16

sma hat geschrieben:Ich persönlich finde es eigentlich recht elegant, dass bei Scala `val` und `var` so ähnlich aussehen. In den Code gesprenkelte `final`-Modifikatoren wie bei Java stören dagegen meine Ästhetik.
Also ich ziehe Übersichtlichkeit und Verständlichkeit der Ästhetik vor..
sma hat geschrieben:Ruby hat sehr wohl Konstanten.
Ja, hatten wir schon. Eben das wäre bei Python auch nett, also in der Implementierung selbst und nicht per Monkey-Patching.
sma hat geschrieben:Dumm nur, dass manchmal derartige statische Typdeklarationen den Code schwerer verständlich machen, als wenn sie fehlen würden.
Also ich kenne keine Beispiele wo die Verwendung von Konstanten den Code komplexer macht, bei Ruby sind es lediglich Großbuchstaben anstatt Kleinbuchstaben, also nur marginale Differenzen, bei C ist es das kleine Wörtchen "const", ansonsten verhält es sich doch relativ unauffällig, hilft aber im Bedarfsfall Fehlern auf die Schliche zu kommen.
fredistdurstig
User
Beiträge: 13
Registriert: Sonntag 18. Januar 2009, 20:08

Jeder Programmierer bekommt eingehämmert, dass das Verwenden von ziemlich ähnlichen Variablennamen *Pfui* ist. So etwas dann in die Syntax einer Sprache zu gießen ist schon bösartig.
Das stimmt so nicht. Die Variablennamen sind hier ja i und j, die habe ich selbst gewählt. Var und val sind ja quasi nur zusätzliche identifier, die dem Compiler sagen, ob eine Variable konstant ist oder nicht.

Der große Vorteil dieser kurzen und einfachen Schreibweise ist der, dass der Programmierer nicht aus Faulheit auf die Verwendung von Konstanten verzichtet - wie es in Java der Fall sein kann. Schließlich muss an jede Variable ZUSÄTZLICH noch das Wort "final" rangeschrieben werden. In Scala ist der Schreibaufwand jedoch derselbe.
Hmm, wenn schon würde ich const verwenden. val und var ist doch viel zu ähnlich!
Sie sind ähnlich. Allerdings hat man das sehr schnell im Blick. Generell wird in Scala sowieso meist mit Konstanten gearbeitet, var ist eher die Ausnahme.

Auf mein Argument, dass Konstanten Seiteneffekte vermeiden, und daher einen funktionalen Programmierstil fördern, wurde hier nicht eingegangen. Anscheinend ist funktionales Programmieren in Python kein großes Thema?

Das man Konstanten in Python "nachprogrammieren" kann war mir schon klar. Mich wundert nur, dass das so nicht in der Sprache vorhanden ist, da es ja keinen weiteren Aufwand bedeutet hätte. Daher muss es ja auch einen ganz konkreten Grund geben, warum auf Konstanten ganz explizit verzichtet wurde.
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

fredistdurstig hat geschrieben: Der große Vorteil dieser kurzen und einfachen Schreibweise ist der, dass der Programmierer nicht aus Faulheit auf die Verwendung von Konstanten verzichtet - wie es in Java der Fall sein kann. Schließlich muss an jede Variable ZUSÄTZLICH noch das Wort "final" rangeschrieben werden.
Also wer schreibfaul ist, ist bei Java an der ganz falschen Adresse oO

Dazu kommt: Zwischen final und val sind 2 Zeichen Unterschied, wobei man bei Java sowieso fließend die Autovervollständigung der IDE nutzt.
Antworten