Python-Leselisten, die den Property-Decorator verwenden

8

Kurze Version

Kann ich eine schreibgeschützte Liste mit Pythons Eigenschaftensystem erstellen?

Lange Version

Ich habe eine Python-Klasse erstellt, die eine Liste als Mitglied hat. Intern möchte ich, dass jedes Mal etwas geändert wird, wenn die Liste geändert wird. Wenn dies C ++ wäre, würde ich Getter und Setter erstellen, die es mir erlauben würden, meine Buchhaltung zu machen, wenn der Setter aufgerufen würde, und ich würde den Getter eine const Referenz zurückgeben lassen, so dass der Compiler mich anschreien würde, wenn ich versucht, die Liste durch den Getter zu ändern. In Python haben wir das Property-System, so dass das Schreiben von Vanilla Getter und Setter für jedes Datenelement (glücklicherweise) nicht länger notwendig ist. Beachten Sie jedoch das folgende Skript:

%Vor%

Ausgabe:

%Vor%

Dies veranschaulicht, dass das Fehlen des Konzepts von const in Python es mir ermöglicht, meine Liste mit der Methode append() zu modifizieren, obwohl ich sie zu einer Eigenschaft gemacht habe. Dies kann meinen Buchhaltungsmechanismus umgehen ( _myList ), oder es kann verwendet werden, um Listen zu modifizieren, die man möglicherweise nur lesen möchte ( _readOnlyList ).

Eine Problemumgehung wäre die Rückgabe einer tiefen Kopie der Liste in der Getter-Methode (d. h. return self._myList[:] ). Dies kann viel zusätzliches Kopieren bedeuten, wenn die Liste groß ist oder wenn die Kopie in einer inneren Schleife ausgeführt wird. (Aber eine vorzeitige Optimierung ist sowieso die Wurzel allen Übels.) Während eine tiefe Kopie verhindern würde, dass der Buchhaltungsmechanismus umgangen wird, würde jemand, der .myList.append() aufrufen würde, stillschweigend verworfen werden, was einige generieren könnte schmerzhaftes Debugging. Es wäre schön, wenn eine Ausnahme ausgelöst würde, damit sie wissen, dass sie gegen das Design der Klasse arbeiten.

Eine Lösung für dieses letzte Problem wäre, das Property-System nicht zu verwenden und "normale" Getter- und Setter-Methoden zu erstellen:

%Vor%

Wenn der Benutzer versucht, append() aufzurufen, sieht dies wie foo.myList().append(8) aus, und diese zusätzlichen Klammern deuten darauf hin, dass sie möglicherweise eine Kopie erhalten und nicht auf die Daten der internen Liste verweisen. Das Negative daran ist, dass es irgendwie unpythonisch ist, Getter und Setter so zu schreiben, und wenn die Klasse andere Listenmitglieder hat, müsste ich entweder Getter und Setter für diese schreiben (Eww), oder die Schnittstelle erstellen inkonsistent. (Ich denke, eine etwas inkonsistente Schnittstelle könnte das geringste Übel sein.)

Gibt es eine andere Lösung, die ich vermisse? Kann man mit Pythons Property-System eine Read-Only-Liste erstellen?

Bearbeiten: Lösung

Die beiden Hauptvorschläge scheinen entweder ein Tupel als schreibgeschützte Liste oder eine Unterklassenliste zu verwenden. Ich mag beide Ansätze. Die Rückgabe eines Tupels vom Getter oder die Verwendung eines Tupels an erster Stelle verhindert die Verwendung des Operators + =, der ein nützlicher Operator sein kann, und löst auch den Buchhaltungsmechanismus aus, indem er den Setter aufruft. Das Zurückgeben eines Tupels ist jedoch eine einzeilige Änderung, was sehr hilfreich ist, wenn Sie defensiv programmieren möchten. Sie sollten jedoch bedenken, dass das Hinzufügen einer ganz anderen Klasse zu Ihrem Skript unnötig kompliziert ist. (Es kann manchmal gut sein, minimalistisch zu sein und davon auszugehen, dass man es nicht braucht.)

Hier ist eine aktualisierte Version des Skripts, die beide Ansätze für jeden, der dies über Google findet, zeigt.

%Vor%

Ausgabe:

%Vor%

Danke, alle.

    
ngb 05.05.2014, 14:12
quelle

3 Antworten

6

Sie könnten eine Methode haben, die einen Wrapper um Ihre ursprüngliche Liste zurückgibt - collections.Sequence könnte beim Schreiben hilfreich sein. Oder Sie könnten tuple zurückgeben - Der Aufwand beim Kopieren einer Liste in ein Tupel ist oft vernachlässigbar.

Wenn jedoch ein Benutzer die zugrunde liegende Liste ändern möchte, kann er dies tun und es gibt wirklich nichts, was Sie dagegen tun können. (Immerhin haben sie direkten Zugriff auf self._myList , wenn sie es wollen).

Ich denke, dass der pythonische Weg, etwas wie das zu tun, darin besteht, zu dokumentieren, dass sie die Liste nicht ändern sollten und dass, wenn ist, dann ihre Schuld ist Ihr Programm stürzt ab und brennt.

    
mgilson 05.05.2014, 14:20
quelle
1
  

trotz der Tatsache, dass ich es zu einer Eigenschaft gemacht habe

Es spielt keine Rolle, ob es sich um eine Eigenschaft handelt, Sie geben einen Zeiger auf die Liste zurück, damit Sie sie ändern können.

Ich würde vorschlagen, eine Liste zu erstellen Unterklasse und überschreiben append und __add__ Methoden

    
hithwen 05.05.2014 14:23
quelle
0

Warum ist eine Liste einem Tupel vorzuziehen? Tupel sind in den meisten Fällen "unveränderbare Listen" - also von Natur aus werden sie als schreibgeschützte Objekte fungieren, die nicht direkt gesetzt oder modifiziert werden können. An diesem Punkt muss man den Setter für diese Eigenschaft einfach nicht schreiben.

%Vor%     
user890167 05.05.2014 14:54
quelle