25

Hinweis: Ich spreche nicht über das Verhindern der Neubindung einer Variablen. Ich spreche über das Verhindern der Änderung des Speichers, auf den sich die Variable bezieht, und von jedem Speicher, der von dort erreicht werden kann, indem den verschachtelten Containern folgt.Warum hat sich Python gegen konstante Referenzen entschieden?

Ich habe eine große Datenstruktur und möchte sie schreibgeschützt anderen Modulen zugänglich machen. Die einzige Möglichkeit, dies in Python zu tun, besteht darin, die einzelnen Teile, die ich ausstellen möchte, tief zu kopieren - in meinem Fall unerschwinglich teuer.

Ich bin sicher, dass dies ein sehr häufiges Problem ist, und es scheint, als wäre eine konstante Referenz die perfekte Lösung. Aber ich muss etwas verpassen. Vielleicht sind konstante Referenzen in Python schwer zu implementieren. Vielleicht tun sie nicht ganz das, was sie meiner Meinung nach tun.

Alle Einsichten würden geschätzt werden.


Während die Antworten sind hilfreich, ich habe nicht einen einzigen Grund gesehen, warum konst entweder schwer zu implementieren oder undurchführbar in Python wäre. Ich denke, "un-Pythonic" würde auch als ein berechtigter Grund gelten, aber ist es wirklich? Python zerlegt private Instanzvariablen (beginnend mit __), um versehentliche Fehler zu vermeiden, und const scheint nicht so anders zu sein.


EDIT: Ich bot nur eine sehr bescheidene Kopfgeld. Ich suche ein bisschen mehr Details darüber, warum Python ohne const endete. Ich vermute, der Grund ist, dass es wirklich schwierig ist, es perfekt zu implementieren. Ich würde gerne verstehen, warum es so schwer ist.

Antwort

10

In PEP 351 schlug Barry Warschau ein Protokoll zum "Einfrieren" jeder veränderlichen Datenstruktur vor, analog zu der Art und Weise, wie frozenset eine unveränderliche Menge bildet. Eingefrorene Datenstrukturen wären hashbar und könnten so als Schlüssel in Wörterbüchern verwendet werden.

Der Vorschlag war discussed on python-dev, mit Raymond Hettinger's criticism am ausführlichsten.

Es ist nicht genau das, wonach Sie suchen, aber es ist das nächste, was ich finden kann, und sollte Ihnen eine Vorstellung von dem Denken der Python-Entwickler zu diesem Thema geben.

+0

Vielen Dank, sehr hilfreich. Während ich mein Interesse nur auf "const" beschränkte, um Bugs zu vermeiden, behandelte dieser Thread ein "nützlicheres"/"breites" Konzept von "concept" als einen hashbaren Datentyp. Trotzdem, sehr interessant zu lesen. – max

8

Es gibt viele Design-Fragen zu jeder Sprache, die Antwort auf die meisten ist "nur weil". Es ist ziemlich klar, dass solche Konstanten gegen die Ideologie von Python verstoßen würden.


Sie können eine schreibgeschützte Attribut Klasse machen, obwohl, descriptors verwenden. Es ist nicht trivial, aber es ist nicht sehr schwer. Die Funktionsweise besteht darin, dass Sie Eigenschaften (Dinge, die wie Attribute aussehen, aber eine Methode für den Zugriff aufrufen) mit dem property Decorator erstellen; Wenn Sie eine Getter-, aber keine Setter-Eigenschaft erstellen, erhalten Sie ein schreibgeschütztes Attribut. Der Grund für die Metaclass-Programmierung ist, dass, da __init__ eine vollständig ausgeformte Instanz der Klasse empfängt, Sie tatsächlich nicht die Attribute zu dem, was Sie in diesem Stadium wollen, festlegen können! Stattdessen müssen Sie sie bei der Erstellung der Klasse festlegen, was bedeutet, dass Sie eine Metaklasse benötigen.

-Code von this recipe:

# simple read only attributes with meta-class programming 

# method factory for an attribute get method 
def getmethod(attrname): 
    def _getmethod(self): 
     return self.__readonly__[attrname] 

    return _getmethod 

class metaClass(type): 
    def __new__(cls,classname,bases,classdict): 
     readonly = classdict.get('__readonly__',{}) 
     for name,default in readonly.items(): 
      classdict[name] = property(getmethod(name)) 

     return type.__new__(cls,classname,bases,classdict) 

class ROClass(object): 
    __metaclass__ = metaClass 
    __readonly__ = {'a':1,'b':'text'} 


if __name__ == '__main__': 
    def test1(): 
     t = ROClass() 
     print t.a 
     print t.b 

    def test2(): 
     t = ROClass() 
     t.a = 2 

    test1() 
+0

Ich schätze diese Informationen, es ist interessant. Aber damit wird das Problem, auf das ich mich bezogen habe, nicht angesprochen: Angenommen, das Attribut ist ein Container. Obwohl das Attribut nicht änderbar ist, kann ich immer noch alle seine Elemente ändern. Kann ich dieses Rezept auch ändern, um ein konstantes Wörterbuch (auf oberflächennaher Ebene) zu übergeben? – max

+1

Frage zu Ihrem Beispielcode: Was würde verhindern, dass jemand t .__ readonly __ ['a'] = 2 tut und Daten verändert? –

+2

@Max: Wenn Sie geschachtelte schreibgeschützte Strukturen haben wollen, dann müssen Sie diese Idee einmal pro Level implementieren, bis Sie auf immutables stoßen. Sie könnten auch Ihre eigene 'dict'-Klasse schreiben, um schreibgeschützte Wörterbücher zu handhaben, indem Sie' getitem' und 'setitem' modifizieren. Es wird nicht nett (oder idiotensicher) sein, weil es nicht so ist, wie man Python schreiben soll. – katrielalex

13

Es ist das gleiche wie mit privaten Methoden: as consenting adults Autoren Code sollte ohne die Notwendigkeit einer Kraft an einer Schnittstelle übereinstimmen. Denn wirklich wirklich Durchsetzung des Vertrages ist hart, und es zu tun, die halb-assed Weg führt zu hackish Code in Hülle und Fülle.

Verwenden Sie Get-Only-Deskriptoren und geben Sie in Ihrer Dokumentation eindeutig an, dass diese Daten sind, die nur gelesen werden sollen. Schließlich könnte ein entschlossener Programmierer einen Weg finden, Ihren Code auf verschiedene Arten zu verwenden, an die Sie sowieso gedacht haben.

+2

Ich mache mir keine Sorgen wegen Hacks. Ich helfe nur, Bugs zu vermeiden. Wenn ich einen bestimmten Container übergebe, rufe ich verschiedene Funktionen auf, die ihren Inhalt (versehentlich) ändern können. Ich würde gerne darüber wissen. – max

+0

Natürlich, wenn die Sprache, die Sie verwenden, Möglichkeiten hat, diese Dinge zu erzwingen, dann sollten diese Funktionen verwendet werden! –

+1

@max Warum sollten wir uns auf zufällige Mutationen als eine wichtige Fehlerquelle konzentrieren? Ich kann mich an kein einziges Mal erinnern, dass ich einen Fehler hatte, weil ich versehentlich etwas verändert habe, das eigentlich konstant sein sollte. Ich erinnere mich jedoch, dass ich hunderte von Malen Code reparieren musste, weil die const-Annotationen nicht übereinstimmten. –

1

Während ein Programmierer, der einen Code schreibt, ein zustimmender Erwachsener ist, stimmen zwei Programmierer, die an demselben Code arbeiten, selten Erwachsenen zu. Vor allem, wenn sie nicht die Schönheit des Codes schätzen, sondern die Fristen oder Forschungsgelder.

Für solche Erwachsene gibt es eine Art Sicherheit, die von Enthought Traits zur Verfügung gestellt wird.

Sie könnten in Constant and ReadOnly Merkmale suchen.

1

Für einige zusätzliche Gedanken, gibt es eine ähnliche Frage gestellt über Java hier:
Why is there no Constant feature in Java?

Bei der Frage, warum Python hat entschieden gegen konstante Referenzen, ich denke, es ist hilfreich, daran zu denken, wie würden sie umgesetzt werden in der Sprache. Sollte Python eine spezielle Deklaration haben, const, um Variablenverweise zu erstellen, die nicht geändert werden können? Warum lassen sich Variablen nicht als float/int/was auch immer deklarieren? ... das würde sicherlich dazu beitragen, Programmierfehler zu vermeiden. Wenn wir schon dabei sind, fügen wir Klassen- und Methodenmodifikatoren wie protected/private/public/etc hinzu. würde dazu beitragen, die Überprüfung des Kompilierungstyps gegen illegale Verwendungen dieser Klassen zu erzwingen. ... schon bald haben wir die Schönheit, Einfachheit und Eleganz verloren, die Python ist, und wir schreiben Code in eine Art Bastard-Kind von C++/Java.

Python übergibt derzeit auch alles als Referenz. Dies wäre eine Art spezieller Pass-by-Reference-aber-Flag-It-to-Prevent-Modifikation ... ein ziemlich spezieller Fall (und wie das Tao von Python angibt, einfach "un-Pythonic").

Wie bereits erwähnt, kann diese Art von Verhalten über die Klassen & Deskriptoren implementiert werden, ohne die Sprache tatsächlich zu ändern. Es kann die Änderung von einem entschlossenen Hacker nicht verhindern, aber wir stimmen Erwachsenen zu. Python nicht unbedingt entscheiden gegen die Bereitstellung dieses als ein inbegriffenes Modul ("Batterien enthalten") - es gab einfach nie genug Nachfrage dafür.

+0

Python verwendet nicht Pass-by-Reference. Es verwendet "Referenzen", aber es übergibt diese nach Wert. Vergleichen Sie 'def foo (x) {x = 10; } Balken = 5; foo (bar); Druckbalken '(Python mit sündhaften Klammern, da Kommentare Zeilenumbrüche/Einrückungen nicht vorgeben, Übergabewerte) mit' void foo (int & x) {x = 10; } /*...*/ int bar = 5; foo (bar); cout << bar; '(C++, pass-by-reference). – delnan

+0

Danke für die Klarstellung. – Gerrat