Für und gegen __slots__

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.
Antworten
BlackJack

Weil kritisieren so'n Spass macht: Warum zum Henker benutzt Du `__slots__`? Wieviele Vokabeln erwartest Du in einer Datei, dass es Probleme mit dem Speicherverbrauch geben könnte?

Insbesondere bei der Vokabelliste, wo ich irgendwie nur eine handvoll Exemplare pro Programmlauf sehe.

Edit (Leonidas): Vom Thread Welches Datenbanksystem? getrennt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackJack hat geschrieben:Warum zum Henker benutzt Du `__slots__`?
[...]
Insbesondere bei der Vokabelliste
Hi BlackJack!

Das ist ein berechtigter Einwand.
In der Klasse ``Vocable`` dachte ich wirklich an einen Geschwindigkeitsvorteil. In der Klasse ``Vocabulary`` bringt es aber (bis auf die Einschränkung, dass man von Außen keine neuen Attribute erstellen kann) gar nichts.

Wir rechnen ja mit bis zu 8.000 Vokabeln. Glaubst du nicht, dass ``__slots__`` in der Klasse ``Vocable`` einen Geschwindigkeitsvorteil bringt?

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

gerold hat geschrieben:Wir rechnen ja mit bis zu 8.000 Vokabeln. Glaubst du nicht, dass ``__slots__`` in der Klasse ``Vocable`` einen Geschwindigkeitsvorteil bringt?
Kann sein und wie gross ist der? Ist der bei 10K Vokabeln spürbar? Beim einlesen von Platte überhaupt vorhanden weil das Programm sowieso schneller ist als die Festplatte Daten liefern kann? Das sieht extrem nach "premature optimization" aus.

`__slots__` ist so eine Sache die ich lieber verschweige, weil sie insbesondere Umsteiger von anderen Sprachen auf dumme Ideen bringt ("final"/"es ist schneller/kleiner"). Solange man kein Problem mit dem Speicherverbrauch hat, sollte man `__slots__` nicht kennen müssen. IMHO.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackJack hat geschrieben:
gerold hat geschrieben:Geschwindigkeitsvorteil
Das sieht extrem nach "premature optimization" aus.

`__slots__` ist so eine Sache die ich lieber verschweige, weil sie insbesondere Umsteiger von anderen Sprachen auf dumme Ideen bringt ("final"/"es ist schneller/kleiner").
Hi BlackJack!

Jein...

Ich schütze mich beim Programmieren gerne selber vor Fehlern. Deshalb setze ich auch ab und an mal ``assert`` ein um z.B. einen übergebenen Parameter zu prüfen oder ich schränke ab und an auch eine Klasse mit ``__slots__`` ein, damit ich den Klasseninstanzen keine falsch geschriebenen Attribute zuweisen kann.
Das hat dann nichts mit Geschwindigkeitsoptimierung zu tun.

Code: Alles auswählen

class GlobalConstants(object):
    __slots__ = ("vorname", "nachname")

gconst = GlobalConstants()
gconst.vorname = "Gerold"
gconst.nachname = "Penz"

gconst.VorName = "Nudlaug" 
#AttributeError: 'GlobalConstants' object has no attribute 'VorName'
Das ist für mich irgendwie auch eine Qualitätskontrolle -- schon während dem Schreiben des Quellcodes.

Deshalb möchte ich ich die Existenz von ``__slots__`` nicht unbedingt verweigern.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hmm, '__slots__' kannte ich bis her nicht. Ich finde die möglichkeit echt gut, das man damit bewirken kann, keine Atribute extern einer Klasse zuzuweisen. Sollte man slots aus diesem grund nicht einsetzen? Gerade deswegen würde eich das gerne _jetzt_ permanent benutzen :) Ich finde der einwand das der Programmierer sleber vor fehlern geschützt wird berechtigt.

Daher verstehe ich deinen Einwand nicht BlackJack :? Wann genau sollte man slots benutzen und warum sollte man tunlichst slotst vereiden? :?

lg
BlackJack

Genau das meine ich mit auf dumme Ideen bringen. Dafür ist `__slots__` nicht gedacht. Vor so etwas warnt einen zum Beispiel `PyLint`.

Auf Objekte mit `__slots__`, auch wenn sie nur geerbt sind, kannst Du keine `weakrefs` setzen, Du kannst sie nicht picklen, in Subklassen ist der "Schutz" dann auch schon wieder weg, die Probleme bleiben aber. Und Mehrfachvererbung funktioniert nicht wenn mehr als eine Klasse `__slots__` besitzt. Man kann die Objekte auch nicht mehr mit beliebigen Attributen dekorieren. Das muss ja nicht immer ein Fehler sein.

Viel zu viele Probleme bzw. Verhaltensänderungen gegenüber normalen Klassen/Objekten. Wie oft hilft Dir das bei Vertippern? Kann man das nicht mit einem Editor lösen der Autovervollständigungen vorschlägt? Und fallen solche Probleme beim Testen nicht sehr schnell auf?

Das mit dem picklen ist im vorliegenden Fall zum Beispiel ein ziemlicher Minuspunkt. Warum sollte man Vokabeln nicht picklen können?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackJack hat geschrieben:nicht picklen [...] Mehrfachvererbung funktioniert nicht [...] Verhaltensänderungen gegenüber normalen Klassen/Objekten
Hi BlackJack!

Ja, das sind gewichtige Gegenargumente. Der Rest war mir zwar ziemlich "Wurst", aber so gesehen muss ich dir zustimmen. Danke für die Aufklärung.

Es scheint so, dass die Gegenargumente überwiegen. Ich werde ``__slots__`` nur mehr gezielt einsetzen wenn es gebraucht wird.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Ich habe mal folgendes Programm auf meinem Rechner laufen lassen:

Code: Alles auswählen

import os
import re

SLOT_RE = re.compile('^ *__slots__.*=')
CLASS_RE = re.compile('^ *class .*:')

def python_filenames(start_path):
    for path, dummy, filenames in os.walk(start_path):
        for filename in filenames:
            if filename.endswith('.py'):
                yield os.path.join(path, filename)


def multifile_iter(filenames):
    for filename in filenames:
        print filename
        lines = open(filename, 'r')
        for line in lines:
            yield line
        lines.close()


def count_classes_and_slots(lines):
    classes = 0
    slots = 0
    for line in lines:
        if CLASS_RE.match(line):
            classes += 1
        elif SLOT_RE.match(line):
            slots += 1
    return (classes, slots)

def main():
    path = '/usr/lib/python2.4'
    print count_classes_and_slots(multifile_iter(python_filenames(path)))


if __name__ == '__main__':
    main()
Das Ergebnis: nur 82 von 17811 Klassen auf meinem System benutzen `__slots__`. Da sind eine ganze Menge "externe" Packages installiert. Wenn ich es auf die "jungfräuliche" Python2.5-Installation loslasse kommen 73 von 3987 Klassen heraus.

Bei den ganzen Zusatzpaketen der 2.4er Installation sind also gerade mal ca. 10 zusätzliche Klassen mit `__slots__`, wenn man unterstellt das sich deren Anzahl in der Standardbibliothek nicht stark verändert hat.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

OK, hast mich überzeugt. Dann lasse ich die Finger weg von '__slots__' da die Gegenargumente gewaltig sind und ich bis her ehe keine Verwendung dafür hatte.

Aber was genau ist den der Vorteil von '__slots__'? Soll man das nur benutzen wenn man im vornherein weis das man es mit z.B. ca. 100.000 Datensätzen zu tun hat?

lg
BlackJack

Der Vorteil von `__slots__` ist weniger Speicherverbrauch pro Objekt. Wenn man `__slots__` angibt, dann wird für jedes Attribut ein fester Platz in einem Array reserviert wo ein Pointer auf das Objekt gespeichert werden kann.

Ohne `__slots__` werden die Attribute in einem Dictionary gespeichert, das immer ein wenig mehr Speicher vorhalten muss als für die Attribute notwendig ist, damit der Zugriff auf die Hashtabelle effizient bleibt.

Ich würde `__slots__` nur einsetzen, wenn wirklich nachweislich zuviel Speicher verbraucht wird. Wenn ein Objekt 1 bis 2 KB verbraucht, und das scheint mir schon relativ gross, dann sind das bei 100000 Objekten "nur" 100 bis 200 MB. Ist zwar schon eine Menge, aber durchaus verkraftbar für heutige RAM-Grössen.

Und selbst dann könnte man noch überlegen, ob man wirklich alle Objekte gleichzeitig im Speicher benötigt oder nicht vielleicht auf Platte ausweichen kann. Die `__slots__` bringen ja nur einen Konstanten Speichergewinn pro Objekt, was Alex Martelli so in Newsgroup Posting von sich gibt: "some tenth of bytes", also nicht mehr als 100 Bytes pro Objekt. Wenn also ein Objekt 2 KB gross ist, dann bekommst Du in den 200 MB nur 5000 Objekte mehr untergebracht. Je grösser das einzelne Objekt desto kleiner der relative Gewinn.

Edit (Leonidas): Diskussion in Welches Dateiformat aufgesplittert, um meine Fehler wieder halbwegs auszubügeln.
Antworten