[Python-de] Designfrage: Von list ableiten?

Stefan Schwarzer sschwarzer at sschwarzer.net
Mo Jun 13 01:19:03 CEST 2011


Hallo Florian,

On 2011-06-12 23:29, Florian Lindner wrote:
> ich habe eine Klasse JobQueue, die eigentlich eine Liste mit ein paar 
> zusätzlichen Methoden ist:
> 
> Voreinstellung fürs Sortieren:
> 
>    def sort(self, reverse = False):
>         """ Sort by priority """
>         self._queue.sort(key = lambda a: a.prio, reverse=reverse)
> 
> Hinzufügen:
> 
>     def put(self, job):
>         job.jid = gen_jid()
>         self._queue.append(j)
>         return job.jid
> 
> filtern:
> 
>     def running_jobs(self):
>         self.sort()
>         return [job for job in self._queue if job.running]
> 
> usw.
> Also nichts großartiges. Der Benutzer der Klasse soll auch problemlos an die 
> verwaltete Liste selber kommen (hier self._queue).

warum hat das Attribut dann einen führenden Unterstrich? ;-)
Der sagt per Konvention aus, dass etwas ein
nicht-öffentliches Implementierungsmerkmal ist.

> Spricht nun irgendwas dagegen die Klasse von list abzuleiten und selber eine 
> Liste zu sein?

Ich würde es eher nicht tun. Jedenfalls nicht, um nur
Implementierungs-Aufwand zu sparen. Das kannst du auch mit
__getattr__ erreichen, indem du _bestimmte_ Zugriffe an das
enthaltene Listen-Attribut "durchreichst". Wenn es nur
wenige Attribute sind (vgl. unten), würde ich allerdings
kein __getattr__ verwenden.

Wenn du deine Klasse von list ableiten lässt, legt das nahe,
dass deine Klasse nicht nur technisch, sondern auch
sinngemäß eine Python-Liste - mit allen ihrer Eigenarten -
ist. Ist es zum Beispiel sinnvoll, in eine _Queue_ Werte mit
"insert" oder Slicing mittendrin einzufügen? Ich denke
nicht. :-) Spätestens, wenn eine Listen-Methode einer deiner
Methoden "widerspricht", hast du ein Problem.

Ich würde von einer Python-Liste zum Beispiel nicht
erwarten, dass die darin enthaltenen Elemente ein
prio(rity)-Attribut haben müssen, wenn ich die sort-Methode
aufrufe. Und das betrifft ja sogar schon eine der von dir
aufgeführte Methode und nicht eine "beiläufig" von list
geerbte.

Ganz entsprechend würde ich auch die enthaltene Liste als
Implementierungs-Detail ansehen und Anwendern deiner Klasse
auch vom direkten Zugriff abraten. Der führende Unterstrich
vermittelt das ja auch und sollte demnach bleiben.

> Damit das Verhalten möglichst wenig beeinflußt wird, würde ich sort so 
> schreiben:
> 
>     def sort(self, key = lambda a: a.prio, reverse = False):
>         """ Sort by priority """
>         super(JobQueue, self).sort(key, reverse)
> 
> (geht das? Einen lambda Ausdruck als default Argument?)

Technisch gesehen ist das möglich, aber ich finde es eher
unübersichtlich. Ich würde die Funktion, die als
Default-Argument dienen soll, eher vorher definieren:

class JobQueue(object):

    @staticmethod
    def _priority_key(key):
        return key.priority

...

    def sort(self, key=_priority_key, reverse=False):
        ...

Das lässt sich meines Erachtens deutlich besser lesen, auch,
wenn der Unterstrich nach dem Gleichheitszeichen ein
bisschen seltsam aussieht.

> Mich würde es interessieren, ob  es da noch irgendwelche versteckten Probleme 
> gibt? Und was prinzipiell Eure Meinung dazu ist.

Ich tendiere dazu, im Zweifelsfall die Schnittstelle
möglichst schlank zu halten. Je weniger Versprechen man
macht, desto einfacher ist es normalerweise, diese
einzuhalten. :-) Beliebig einfach geht's natürlich auch
nicht, denn schließlich möchte man dem Aufrufer ja eine
Funktionalität anbieten, damit dieser diese nicht selbst
implementieren muss.

Viele Grüße
Stefan


Mehr Informationen über die Mailingliste python-de