Klassenkonstruktion: __init__, __slot__, __dict__ + mehr

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
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

Hallo zusammen,
nach einiger Suche und einiger Diskussion z. B. über die regelkonforme Verwendung einer Klasse (anwendungsbezogen, u. a. hier viewtopic.php?f=1&t=53538&start=30 ) möchte ich die Konstruktion einer Klasse gerne ohne bestimmten (eher verwirrenden) Verwendungszusammenhang etwas aufhellen.

Nützlich ist dies auf jeden Fall, unabhängig davon, wie es dann eingesetzt wird.

Grundvorstellungen und Begriffe sind auf meiner Seite vorhanden - meine technische Sprache ist jedoch in keinster Weise gefestigt und führt gerne auch zur Konfusion :?

Es fehlt an Syntax und Konstruktions know how - dazu werden zur Vermeidung von Redundanz noch interne, reflexive calls erforderlich - das ist viel zu viel für mich aktuell..

Die Klasse soll entlang der Bemerkungen so ähnlich aussehen:

Code: Alles auswählen

class platzhalterName:
	#  keine Attribute hier in der Definition
	
	def __init__(self, name):
		self.attr1 = 'val1'
		self.attr2 = 'val2'
		.
		
		# dabei sollen die attr1.. und val1.. Paare
		# aus dem klassenintern geführten __dict__
		# eingelesen und zugewiesen werden
		
		# um dies zu erreichen wird das __dict__
		# vorher mit arg1: val1 etc. Werten "gefüllt"
		# bzw. definiert
		
		# das __dict__ ist dann 
		# der Ur-Ursprung der Attribute
		# einer Klasseninstanz
		
		# dazu kommt dann noch eine Einschränkung
		# der Attribute per Definition der __slots__
		
		# ob und wie sich __slots__ nun überschneiden
		# mit dem __dict__ - kein Plan...
Wenn das mal so weit gelungen wäre, ist die Fertigstellung verm. auch nicht mehr weit..

Hilfestellung, vor allem die Reflexivität und die Abhängigkeit der Anforderungen voneinander betreffend, könnten "schlaflose Nächte" erinigerrmaßen verhindern, hoffe ich :)
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Wenn man __slots__ definiert, dann gibt es kein __dict__. Man macht das typischerweise um Speicherplatz zu sparen, wenn man sehr viele kleine Objekte anlegen will.

Ohne __slots__:

Code: Alles auswählen

>>> class C:
...     def __init__(self, x):
...         self.x = x
... 
>>> c = C(1)
>>> c.x
1
>>> c.__dict__
{'x': 1}
Mit __slots__:

Code: Alles auswählen

>>> class C:
...     __slots__ = ('x',)
...     def __init__(self, x):
...         self.x = x
... 
>>> c = C(1)
>>> c.x
1
>>> c.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__dict__'. Did you mean: '__dir__'?
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Nachtrag:

Ohne __slots__:

Code: Alles auswählen

>>> c.y = 2
>>> c.__dict__
{'x': 1, 'y': 2}
Mit __slots__:

Code: Alles auswählen

>>> c.y = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'y'
Das heißt, ohne __slots__ kann man nach Belieben zur Laufzeit neue Attribute zu einem Objekt hinzufügen, mit __slots__ geht das nicht, da sind nur die dort genannten Attributnamen möglich.
In specifications, Murphy's Law supersedes Ohm's.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@pillmuncher
Ok, danke!

Dann versuch ich erst mal, das Ganze ohne __dict__ zu skizzieren
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Wozu?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Sowohl `__dict__` als auch `__slots__` sollten IMHO konkret im Normalfall eigentlich gar nicht interessieren. `__dict__` ist mehr oder weniger ein Implementierungsdetail, das man nur sehr selten bis gar nicht direkt braucht/verwenden sollte. Man umgeht damit die anderen ”Hooks” die Python bietet um sich in die Funktionsweise vom Punktoperator einzuklinken. `__dict__()` muss nicht alle Attributnamen und Werte enthalten, die das Objekt als API anbietet. Und um `__slots__` würde ich mich auch erst kümmern wenn das tatsächlich notwendig wird. `__dict__` ist ganz nett zu wissen, dass Wörterbücher unter der Haube bei Objekten für den Namensraum verwendet werden, aber so wirklich eine praktische Bedeutung zum schreiben von Klassen hat das jetzt nicht.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@sparrow
sparrow hat geschrieben: Freitag 3. Dezember 2021, 23:22Wozu?
"__slots__" war mir im Gegensatz zu "__dict__" praktisch unbekannt - so lerne ich, das anzuwenden. Es interessiert hier vor allem die Frage: welche möglichen Restriktionen stellt Python im Zusammenhang mit einer Klasse zur Verfügung?

@__blackjack__
Sowohl `__dict__` als auch `__slots__` sollten IMHO konkret im Normalfall ...
Abgesehen davon, dass mir nur Teile von dem Gesagten verständlich sind, kannn es fürs praktische Kodieren durchaus sein, dass z. B. __slots__ nicht oder kaum verwendet werden.

Nachdem unter anderem Du aufgezeigt hast, dass es eine ganze Reihe von nicht immer leicht erkennbaren Fallstricken zumindest für Anfänger gibt, versuche ich testhalber eine weitgehend "zugenagelte" Klasse zu erzeugen.

So weit bin ich jeztz gekommen:

Code: Alles auswählen

import os
import json

class dummyClass:
    # define __slots__
    __slots__ = [
        'attr1',
        'attr2',
        'attr3',
        'data_path',
        'save_data',
        ]

    def __init__(self):
        self.data_path = '.\\dummy_f_dfg_ff__Y_TmP_mydata.json'

        if not os.path.exists(self.data_path):
            # create default data
            self.attr1 = 'val1'
            self.attr2 = 'val2'
            self.attr3 = 'val3'
            
            # store data in dict
            self.save_data = {
                'attr1': 'val1',
                'attr2': 'val2',
                'attr3': 'val3',
                }

            # write data to disc
            with open(self.data_path, 'w') as f:
                json.dump(self.save_data, f)
        else:
            # read data from disc
            with open(self.data_path, 'r') as f:
                self.save_data = json.load(f)

            self.attr1 = self.save_data['attr1']
            self.attr2 = self.save_data['attr2']
            self.attr3 = self.save_data['attr3']

# create instance x of dummyName,
x = dummyClass()

print(f'instance: {x}')
print()
print(x.attr1)
print(x.attr2)
print(x.attr3)
print()
print(x.data_path)
print(x.save_data)
print()

x.atzr1 = 'restriction-test'
Abgesehen davon, dass Teile des codes noch viel zu "extensiv" geschrieben sind, fehlt hier zum vollständigen "Zunageln" noch das Verhindern einer Attribut-Zuweisung unter Umgehung einer dafür noch zu schreibenden Methode.

Bei jedem update wären ja die save_data neu zu erzeugen und zu dumpen.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Ja, __slots__ war die unbekannt. Und das war auch gut so.
In einem der vielen Threads, die du erstellt hat und die sich am Ende um das selbe drehen, hat mal jemand gesagt: du versuchst Probleme für die Lösungen zu finden, die zu sehen glaubst. Oder so ähnlich. So kommt mir das tatsächlich vor.

Ich habe __slots__ genau nie gebraucht.
Vielleicht solltest du erst einmal einen Zugang zu OOP finden anstatt dich mit Sprachdetails auf der Ebene auseinander zu setzen. Manchmal hilft auch einfach mal sich den Code anderer Projekte anzuschauen.
Wenn du absolute Restriktion und Klassen möchtest, dann wirst du mit Python auf Dauer schlicht nicht glücklich.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@__blackjack__
verzeih - die 50% waren irreführend - gemeint war: ...welche "hooks" meinst du konkret?

@sparrow
Keine Frage, man muss __slots__ nicht nutzen müssen..
Was die Python-Liebe betrifft: so langsam macht das auch Spaß - die ersten Hürden sind jedoch recht hoch für mich

Betreffend dem Abfangen von Wert-Zuweisungen an die Attribute funktioniert dies zwar:

Code: Alles auswählen

def __setattr__(self, prop, val):
	super().__setattr__(prop, val)
aber ich schaff's bisher nicht, aus dieser Methode heraus die Attribute auszulesen um das update in die Datei zu schreiben - irgend etwas mach ich aus Nicht-Verständnis heraus falsch

Auch mit etwas wie ... __dict__.__dict__ ... kam ich nicht weiter.

Allerdings gefällt mir das Ganze nun nicht mehr besonders, weil der Nachvollzug spätestens bei super()... aufhört - mal sehen, was am Ende dann übrigbleibt bzw. verwendet wird.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du verzettelst dich hier in voellig unnoetigen Uebungen. Leg deine Attribute an wie jeder andere auch, und speicher das Objekt einfach durch die Auflistung der bekannten Attribute. So wie an anderer Stelle auch schon vorgemacht.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

ulipy hat geschrieben: Sonntag 5. Dezember 2021, 08:51 Keine Frage, man muss __slots__ nicht nutzen müssen..
Verzeihung, aber nein: Du darfst __slots__ nicht benutzen. Jedenfalls noch nicht. Noch lange nicht. Wenn Du irgendwann in ein paar Jahren (!) mal Python so gut kannst, daß Du damit größere Datenmengen (im Sinne von: größer als ein Gigabyte Rohdaten) verarbeiten kannst und dabei feststellst, daß der Arbeitsspeicherbedarf zur Laufzeit Deines Python-Programms größer ist als das, was Du gerade an RAM zur Verfügung hast, dann, aber erst dann, darfst Du Dich daran erinnern, daß es da doch irgendwie so etwas wie __slots__ gegeben hat. Bis dahin solltest Du das Wort und dessen Existenz vergessen.
ulipy hat geschrieben: Sonntag 5. Dezember 2021, 08:51 Was die Python-Liebe betrifft: so langsam macht das auch Spaß - die ersten Hürden sind jedoch recht hoch für mich
Die Hürden sind deswegen so hoch für Dich, weil Du versuchst, den vierten Schritt vor dem ersten zu machen. Der erste Schritt wäre, die Sprache und deren Anwendung zu verstehen, der zweite, danach PEP8 und PEP20 zu lernen und sie so lange sklavisch zu befolgen, bis Du kompetent und erfahren genug bist um zu wissen, wann und aus welchen guten Gründen Du davon abweichen darfst. Der dritte Schritt ist, Erfahrung mit der Sprache zu sammeln, bis Du Dich damit wirklich, wirklich sicher fühlst. Und erst dann folgt der Schritt, den Du jetzt schon vorweg zu nehmen versuchst, nämlich: Dir Gedanken über die Optimierungen und Eleganz zu machen.

Bis Du zum vierten Schritt kommst, wird zweifellos noch ein wenig Wasser den Rhein hinunter fließen, aber trotzdem möchte ich Dir dazu noch einen Ratschlag geben, den Du Dir jetzt bitte erst einmal nur notierst oder bookmarkst und erst dann wieder hervorholst, bis Du tatsächlich mal vor einem Python-Programm mit Optimierungsbedarf stehst. Manchen, ich vermute sogar vielen Python-Entwicklern passiert das übrigens niemals in ihrem Entwicklerleben... aber kommen wir zu meinem Ratschlag.

Die Optimierung von Programmen führt so gut wie immer zu Beeinträchtigungen, und zwar üblicherweise zunächst der Lesbarkeit und Verständlichkeit. Deswegen darf man Optimierungen nur dann machen, wenn sie sich tatsächlich auch lohnen, also: wenn die Vorteile der Optimierung deren Nachteile deutlich und klar überwiegen. Erschwerend kommt hinzu, daß so ein Compiler und / oder Interpreter bestimmte Optimierungen vornehmen und der Code, den Du in Deinem Editor siehst, nicht notwendigerweise mit dem Code identisch ist, der tatsächlich auf Deinem Rechner ausgeführt wird. Aus diesen Gründen gibt es zum Thema Optimerung drei wichtige Leitsätze:
  • "Premature optimization is the root of all evil" (Donald E. Knuth)
  • "Measure, don't guess" (Kirk Pepperdine)
  • "Make it work, make it right, make it fast" (Kent Beck)
Im Kern sagen diese Leitsätze vor allem, daß blinde Optimierungen unsinnig und kontraproduktiv sind, die Optimierung eines Programms einer der letzten Schritte in der Entwicklung ist und dieser Schritt nur unter zwei Umständen unternommen werden sollte: erstens, wenn das Programm nicht die benötigte Performance aufweist oder zuviele Ressourcen verbraucht, und zweitens dann, wenn man gemessen hat, welche Teile des Programms überhaupt problematisch sind. Wer sich nicht daran hält, investiert nahezu immer am falschen Ende.

Nun, wie oben schon erwähnt: von der Optimierung von Programmen bist Du noch weit entfernt, also konzentrier' Dich jetzt erst einmal auf die Schritte eins, danach zwei, und dann drei (Du darfst die Schritte eins und zwei zwar kombinieren, aber Schritt zwei erschließt sich meistens erst dann, wenn man Schritt eins verinnerlicht hat.) Dabei wünsche ich Dir viel Spaß, Erfolg, und Glück.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@LukeNukem
Danke für die umfangreichen Ausführungen, welchen ich mich - abgesehen von "Insider"-Detailkenntnissen praktisch uneingschränkt anschließen kann!

Diese treffen jedoch in der Hauptsache nicht das zu Grunde liegende Problem, d. h., den Ursprung und Kern der Fragestellung(en).

Zur Verständigung und Verständnis vergleiche ich es am besten mit

"Das Problem des Handlungsreisenden"

aus der Mathematik. Vielen hier wird es bekannt sein, ansonsten bei Interesse hilft - wie so oft -> Wikipedia.

Abgesehen davon, dass Vergleiche in aller Regel "hinken", kann es doch so aufgezeigt werden.

Im dritten Lebensabschnitt steht mir also die nachdringlich empfohlene Option der Ausbildung / des Berufswegs (entspricht: herkömmliche mathematische Konzepte für den Handlungsreisenden und sein Problem) nicht zur Verfügung - es wird infolgedessen zwangsläufig ein andersgeartetes Herangehen benötigt.

Nicht unähnlich den existierenden mathematischen Lösungsansätzen wurden nun auf dem direktest möglichen Wege Lösungen angedacht (praktisch und theoretisch) in Teilen wieder verworfen, erneut projektiert etc.., um in Folge zu einem als plausibel erscheinenden, pragmatischen Weg (Handelsroute) zu gelangen.

Dieser Weg (sprich Vorgehensweise) beinhaltet nach diesen Exkursen nun:

Erhöhte Bewusstheit betreffend:
  • Python-Philosophie, Geschichte und Status
  • damit verbundene "Freiheiten", "Gefahren" und "Eigenheiten" für die Planung und den Programmierer
  • Grenzen und pragmatische Möglichkeiten für genau meine Person in der Umsetzung "einfacherer" Programme
Dies sind die "eigentlichen" Punkte, die nicht einfach übernommen werden konnten von Menschen mit wesentlich anderen Vorausetzungen, welche persönlich zudem unbekannt sind - immenses Vertrauen wäre erforderlich gewesen, um etwas einfach "abnicken" oder befolgen zu können.

Voraussichtlich hätte ein tiefer Blick in die Augen des "Godfather of Python" am Stammtisch genügt, wenn er gesagt hätte: "Lass hier einfach die Finger davon, machs so, oder so.." :D

Jedenfalls bin ich sehr dankbar dem gesamten Forum gegenüber und vielen, die teilweise mit fast unermüdlicher Geduld und know-how zu den Themen beigetragen haben!

Zum Thema (Klassenkonstruktion: __init__, __slot__, __dict__ + mehr) selbst gibt es von meiner Seite praktisch nichts Weiteres beizutragen. In diesem Falle würde ich das selbst als eher "sinnlos" ansehen - es wird ja nicht weiter verfolgt.

Praktisch, die Themenaufteilung hier betreffend:
Ich versuche, die allgemeineren und die spezifischeren Themen / Fragen etwas auseinander zu halten - es erscheint sonst manchmal fast unmöglich, fokussiert zu bleiben.
Antworten