Python 'Benzingespräche'

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

rogerb hat geschrieben: Mittwoch 1. Dezember 2021, 17:24 ...
Warum stehen die Initialwerte nicht schon vorher in einer Konfigurationsdatei oder "auf Platte", was das auch immer bedeutet: Initialwerte lesen, vom Benutzer ändern lassen, neue Werte zurückschreiben.
...
Fest-"Platte" - es gibt ja heute kaum mehr welche... - die im M.2-Format sind ja fast "verboten" schnell...

Das Programm soll als Script ohne weitere Datei ausreichen, so der Plan.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

__blackjack__ hat geschrieben: Mittwoch 1. Dezember 2021, 18:40 ...
@ulipy: Ich sehe nicht, dass `DEFAULT_SETTINGS` für alle Zeiten obsolet wird nach dem ersten Programmlauf. Das sind immer noch die Grundeinstellungen, die auch immer noch gebraucht werden, wenn die Einstellungsdatei mal nicht da ist, absichtlich oder wegen einem ”Unfall”. Manchmal möchte man als Benutzer, der ein Programm konfiguriert und da Probleme hat, oder aus anderen Gründen, wissen was die Grundeinstellung ist, an der man irgendwann mal etwas verändert hat.
...
Hallo @__blackjack__,
das trifft de facto schon zu, dass die defaults irgendwann doch wieder benötigt werden (eben z. B: nach einem "Unfall"), auch der Speicherbedarf ist kein Hindernis..

Derzeit steht das einfach nicht an - es ist von zu geringer Bedeutung und der "Schaden" ist ohne Mühe überschau- und handlebar.

Die Problemstellung ist eher akademischer Natur:
soll eine dermaßen winzige Aufgabe tatsächlich in u. U. sogar drei Teile zerrissen werden? ich bin immer noch der Überzeugung, dass dies dem Code nichts nützen würde in Sachen Konformität.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da wären wir wieder an dem Punkt wo Du Sachen offenbar anders bewertest als alle anderen. Das Problem ist IMHO auch nicht akademischer Natur. Das ist sehr praxisrelevant ob sich Programmierer an allgemeine Konventionen halten und verständlichen und wartbaren Code schreiben oder eben nicht. Auch bei vermeintlich kleinen Programmen. Die bleiben nicht immer klein, und in x Jahren hat man dann selber Probleme mit angehäuftem „technical debt“ oder jemand anderes darf sich damit herum ärgern.

Ich sehe auch nicht so wirklich was das Problem ist. Was für riesengrosse Arbeit musst Du denn da investieren und wie viele KLOC Code sind das denn, die die Länge und Ausdauer und Zeit aufwiegen würden, die Du hier in diese fruchtlose, auf zwei Themen verteilte Diskussion steckst? Wir reden doch hier von Code der Art: Schaue ob Datei vorhanden, wenn nein lege Datei aus Defaultwerten an. Dann lade die nun auf jeden Fall bestehende Datei. Und das ist der Ablauf der in jedem Fall passieren muss. Wir reden also eigentlich nur davon ob die KEY_SETTINGS als Konstante oben im Modul sind, oder irgendwo mitten im Code stehen — Anzahl der Zeilen von dem ganzen Sums ist letztlich *gleich*!
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@all
Diese "Sache" ist dank eurer Geduld und Hilfe endgültig durch!

Als Freund von Minimalismus und Neuling zu Python konnte ich es nicht umgehen, beharrlich nach einer sinnvollen, minimalistischen Lösung für ein scheinbar "kleines" Problem zu suchen.

Eine Abweichung von "in der Regel" befolgten Konventionen wäre dann vorgezogen worden, wenn dieses Herangehen auch nur ein winziges Fleckchen "pythonischen Boden" gefunden hätte, auf dem es hätte aufgebaut werden können - hat es aber nicht..

Im Sinne der Hilfestellung war dies dadurch in keiner Weise sinnlos - dem Neuling wurde aufgezeigt, was in diesem Sinne "nicht geht"..

Um gedanklich zunächst eine (neue) Pseudo-Skizzierung zu machen war die letzte Nacht ja lange genug :)

Ich stelle sie dann hier rein um zu sehen, ob der Ansatz 'pythonisch' in Ordnung wäre..
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

Bin nun auf der Suche nach alternativen Möglichkeiten der Umsetzung dieses Themas "settings".

Wäre denn eine Funktion oder eine Klasse zur besseren Kapselung das "Richtige"?

Eine Klasse würde im Umkehrschluss eine 180° - Wende zum bisherigen bedeuten: vom dict [ auf Ebene main() ] --> Klasse.

Vor einer konkreten Umsetzung wäre Feedback zu den damit verbundenen Fragen toll!

Zwei Randbemerkungen dazu:
  • die "Distribution" verwendet außer dem script selbst keine weitere Datei
  • default-settings könnten dann - so die aktuelle Denke - direkt in der Klasse selbst niedergelegt werden
Der code hier ist zwar funktional, jedoch sehr umständlich geschrieben und mit diversen Wiederholungen gespickt - "von Hand" halt, nicht mal "zu Fuß"...

Er kann sicherlich - auch lesbar für mich als Anfänger - um ca. 50% reduziert werden.

Code: Alles auswählen

import json

class MyClass:
    def __init__(self, name):
        # use function objects to set
        # defaults of key - value pairs?
        self.tmp = {} # key-val container

        self.demo_flag = True
        self.tmp['demo_flag'] = self.demo_flag

        self.any_item = 'some text'
        self.tmp['any_item'] = self.any_item

        with open('.\\df dfg ff _Y_TmP_mydata.json', 'w') as f:
            json.dump(self.tmp, f)

    def update(self, key, val):
        f = open('.\\df dfg ff _Y_TmP_mydata.json', 'r')
        self.tmp = json.load(f)

        if key == 'demo_flag':
            self.demo_flag = val
        elif key == 'any_item':
            self.any_item = val
        else:
            print('ERROR')
            quit()

        f.close()

        f = open('.\\df dfg ff _Y_TmP_mydata.json', 'w')
        json.dump(self.tmp, f)
        f.close()

# create instance x of MyClass,
# named "settings"
x = MyClass('settings')

print(f'instance: {x}')

print(f'x.demo_flag: {x.demo_flag}')

key = 'demo_flag'; val = False
x.update(key, val)

print(f'x.demo_flag: {x.demo_flag}')

quit()
Was meint ihr?

Viele Grüße
Uli
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@ulipy,

ich verliere langsam den Überblick aber basierend auf dem was ich mitgelesen habe:

Es besteht kein Grund dafür eine Klasse anzulegen. Meiner Meinung nach ist ein separates Modul, welches sich nur im diese Funktionalität kümmert geeignet. Darin liegen die nötigen Funktionen und die Konstante mit den Voreinstellungen.
Bei jedem Programstart wird geprüft, ob die Datei mit den Benutzereinstellungen vorhanden ist. Wenn nicht, wird er danach gefragt und die Datei wird neu erzeugt.

Aber da du aus irgendeinem Grund das gesamte Programm in ein Modul schreiben willst müsste das alles darein. Das wird schnell unübersichtlich. Eigentlich sollte der Code thematisch in Module unterteilt werden.
Zur Distribution kann man ja ein Paket erstellen. Dann hat man eine einzige Datei zum Verteilen.

Zu deinem Code (Meine Meinung):
Das Attribut name wird nicht verwendet, kann also weg.
Der Kommentar im Methoden-, Funktionen-, Modul- oder Klassenkopf heißt docstring und sollte in dreifache Anführungszeichen gesetzt werden, nicht das Kommentar Symbol #.
Datei-Operationen immer im with-Kontext
Dateinamen in einem Klassenattribut oder einer Konstanten auf Modulebene speichern um sie allen Instanzen zum Lesen zur Verfügung zu stellen.
Pfade als Path-Objekt anlegen.
An die Update-Methode würde ich einen fertigen Dictionary übergeben, der wird mit dem aus der Datei geladenen Dictionary auf Gleichheit der Schlüssen geprüft und dann in die Datei geschrieben.
quit() nicht mitten im Script aufrufen, wenn überhaupt.
Statt print("Error") Exception handling verwenden.
Keine Leerzeichen in Dateinamen verwenden.
Encoding beim Lesen und Schreiben von Textdateien angeben.
Aussagekräftige Namen verwenden.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@rogerb: ab wann etwas unübersichtlich wird, ist eine fließende Grenze. Es spricht erst einmal nichts generelles dagegen, alles in einer Datei zu behalten.
Ich sehe im ganzen Code keinen Kommentar, der sich als Docstring eigenen würde.

@ulipy: tmp ist selten ein sinnvoller Variablenname, und garantiert nie ein sinnvolles Attribut. Zumal Du dieses Attribut auch nirgends als solches verwendest, sondern nur immer als lokale Variable.
MyClass ist ein sehr schlechter Klassenname, warum nicht `Settings`?
Welchen Zweck hat der `name`?
In Python steckt man niemals zwei Anweisungen in eine Zeile, auch wenn es ; gibt, benutzt man ihn nicht.
Die json-Datei wird bei jedem Programmstart neu erstellt. Somit ist sie eigentlich überflüssig.
Eine Konfigurationsdatei liegt üblicherweise irgendwo zentral und nicht im aktuellen Arbeitsverzeichnis.

Ich habe immer noch nicht verstanden, wie Du diese Daten nutzen willst. Warum werden die im Programm geändert? Was steht da konkret drin? Settings werden normalerweise von außen verändert. Der einzige Grund, warum DEFAULT-Werte im Programm stehen ist, dass man beim ersten mal eine solche Datei anlegen kann, falls sie noch nicht existiert.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Sirius3 hat geschrieben: Freitag 3. Dezember 2021, 08:55 Ich sehe im ganzen Code keinen Kommentar, der sich als Docstring eigenen würde.
Das ist richtig. Für die __init__() Methode passt das nicht. Ich würde aber dennoch bei einem Docstring als Erklärung für die Klasse bleiben und entsprechend verschieben.
Bzgl. der Aufteilung, würde ich versuchen soweit wie möglich die Aufteilung vorherzusehen und auch von Anfang an kleine Code-Abschnitte schon abtrennen. Das hier ist für mich eine separate Funktionalität die nichts mit der Hauptfunktionalität zu tun hat.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@ulipy: Ich stelle mal den Sinn der Klasse in dieser Variante in Frage. Denn auf den ersten Blick scheint es so, als würdest du die Funktionalität eines dicts nachbauen, erweitert um das Dumpen. Da dads Dumpen in diesem Fall eher kontraproduktiv ist, weil die Datei immer wieder überschrieben wird, könnte man das ganze auch als dict abbilden und das dann dumpen.

Spannend wird das sowieso erst, wenn du es tatsächlich verwendest. Denn etwas in eine Klasse zu kapseln bewahrt einen ja keineswegs davor, die Intanzierung dann eine globale Variable zu binden.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

generell zum Verständnis:
  • es geht in diesem Entwicklungsschritt einzig um die Frage: Klasse oder nicht Klasse?
  • der aktuelle Status des codes zeigt mangels sicherer Terminologie (noch auf extrem umständliche Weise) lediglich die geplante Verwendung der Klasse auf
  • d. h., von allen Bäumen, welche die Sicht auf den Wald verstellen, kann ohne "Schaden" abstrahiert werden
  • auf irgend ein zum script externes Element möchte ich dabei ganz klar verzichten - die Begründung dafür führt jedoch von dieser Sache ab. Falls dies aus py-sprachbedingten Gründen nicht "machbar" wäre, wäre die Verwendung von Python "falsch"
-------------------------------------------------------------------------------------------

@rogerb
Bei jedem Programstart wird geprüft, ob die Datei mit den Benutzereinstellungen vorhanden ist. Wenn nicht, wird er danach gefragt und die Datei wird neu erzeugt.
- genau so ist das geplant

@Sirius3
In Python steckt man niemals zwei Anweisungen in eine Zeile, auch wenn es ; gibt, benutzt man ihn nicht.
Die json-Datei wird bei jedem Programmstart neu erstellt. Somit ist sie eigentlich überflüssig.
- ja, das ist eine der Krücken, die derzeit im aktuellen Code-Schnippsel steht - das kann dort "niemals nicht" verbleiben...
Dieses aktuell nachrangige Problem ist einer der hzu abstrahierenden "Bäume" und führt zu einem nachfolgenden Punkt - spare den vor einer Entscheidung erstmal aus

- ja, ich hatte versehentlich das vorrangige Lesen der json ausgelassen...

Reichen denn diese Angaben zu einer Vorentscheidung aus oder fehlt noch etwas Wichtiges dazu?
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@ulipy,

ich glaube das wurde dir schonmal gesagt, aber es scheint du möchtest es auf deine Weise machen und von allen anderen hier irgendwie die Bestätigung bekommen das das Okay ist...
Ich denke ein wichtiger Schritt beim Programmieren lernen ist es auch eigene Fehler zu machen und daraus zu lernen.
Ich glaube auch nicht alles nur weil jemand sagt: "Das ist so". Das Ergebnis eines solchen Verhaltens ist, dass man entweder tatsächlich eine bessere Lösung findet oder sich eines besseren belehren lassen muss.
Beides sind positive Ergebnisse.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@rogerb
damit wir nicht aneinander vorbeireden: auf welchen Punkt beziehst du dich genau?
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@ulipy: um es ganz klar zu sagen: in diesem Entwicklungsschritt keine Klasse!
Solange Du hier nicht erklären kannst oder willst, was Deine eigentliche Anwendung ist, wird Dir hier auch niemand etwas anderes sagen können. Klassen sind kein Selbstzweck, sondern nur dann sinnvoll, wenn die Komplexität eine Klasse zu definieren an anderer Stelle mehr Komplexität einsparen läßt. An zwei Dummy-Anwendungszeilen Code läßt sich das nicht zeigen.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@Sirius
Danke! Deine klare Aussage hilft hier weiter - der Zweck des kleinen Programms und die Verwendung von Objekten im Programm wird auf jeden Fall hier noch dargestellt.

Aus privatem Bedarf heraus ist - wie so oft - die Absicht zu einem öffentlich (manuell oder als cron-job ) verwendbaren Werkzeug für die Erstellung von Versionskopien täglicher Arbeit am Rechner geworden - GPL "natürlich".

Diese Sache ist mit Sicherheit nicht neu - es geht um die Erstellung einer passgenauen und dennoch flexiblen Variante für eine sehr begrenzte Reihe möglicher Nutzer.

Da ich persönlich "das Rad" ganz gerne auch mal neu erfinde, kann sich das ggfs. auch als Übung in Python darstellen..
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

ulipy hat geschrieben: Freitag 3. Dezember 2021, 11:34 @rogerb
damit wir nicht aneinander vorbeireden: auf welchen Punkt beziehst du dich genau?
ulipy hat geschrieben: Freitag 3. Dezember 2021, 12:32 Da ich persönlich das Rad ganz gerne auch mal neu erfinde, kann sich das ggfs. auch als Übung in Python darstellen..
Genau das meinte ich.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

ulipy hat geschrieben: Mittwoch 1. Dezember 2021, 17:05 Du gibst nun dem user beim initialen Start die Möglichkeit, keinen oder auch jeden dieser Werte an seine Bedürfnisse anzupassen (kein GUI, Kommandozeile).
Dann schreibst du das fest auf Platte.
Das, was danach von Platte gelesen wird, sind per Definition user settings.
Aus dem script wird das danach "nie mehr" benötigt oder gelesen.
Es gibt viele Wege zum Ziel. Ich gebe Dir mal ein Beispiel, wie es umgesetzt werden könnte:

Code: Alles auswählen

from configparser import ConfigParser
from pathlib import Path


SETTINGS_FILE = Path().resolve().parent / "configuration.ini"
SETTINGS_FILE_ENCODING = "utf-8"


def get_settings(settings_file):
    """
    Takes a Path-instance as settings_file and
    returns a dictionary with the settings.
    """
    defaults = {
        'del_orphaned': False,
        'demo_only': False,
        'hide_versions': False,
        'symlinks_dirs': False,
        'symlinks_files': False,  
    }
    config = ConfigParser(defaults=defaults)
    if settings_file.exists():
        with open(settings_file, "r", encoding=SETTINGS_FILE_ENCODING) as fobj:
            config.read_file(fobj)
            defaults = dict(config.items("DEFAULT"))
    else:
        with open(settings_file, "w", encoding=SETTINGS_FILE_ENCODING) as fobj:
            config.write(fobj)
    return defaults


settings = get_settings(SETTINGS_FILE)
print(settings)
Du hast hier eine Funktion "get_settings" die Dir die Settings als dictionary zurück gibt. Die Settings werden dabei aus der Datei "configuration.ini" gelesen, die sich im gleichen Verzeichnis wie Dein Skript befinden muß. Existiert diese Datei nicht, dann wird sie angelegt und die default-Werte werden verwendet. Existiert die Datei, dann überschreiben dortige Inhalte die default-Werte.

Das lässt sich alles auch noch schöner und umfangreicher machen (z.B. verschiedene Verzeichnisse, die der Reihe nach für eine .ini Datei durchsucht werden etc.) aber damit kannst Du schon mal ein wenig herumspielen.

Sicherlich erfordert diese Möglichkeit nun eine optionale(!) settings-Datei, aber das ist durchaus sinnvoll.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@alle
Zum Verständnis:
Es geht nicht darum, irgendetwas stur zu verfolgen.

Aus den meisten Beiträgen war m. E. herauszulesen, dass die Informationen des Für und Wider der Verwendung einer Klasse im vorliegenden Fall nicht ausreichend sind.

Dies ist verstanden und hat auf meiner Seite dazu geführt, diese Verwendung zunächst als Option weiter zu entwickeln. Später erst wird man sehen können, was genau an dieser Stelle das "Richtige" ist.

@kbr
was für mich hier nicht klar ist:

Code: Alles auswählen

defaults = {
        'del_orphaned': False,
        'demo_only': False,
        .
        .
"defaults" ist eine Konstante, allerdings "nur" innerhalb einer klasse verwendet.
Würde "defaults" dann tatsächlich nicht wie "DEFAULTS" geschrieben?
Das nur der Vollständigkeit halber.


@Sirius3
sag bitte zur Linderung meiner Schmerzen :D , ob dir wenigstens Zweifel gekommen waren in der Interpretation dieser Platzhalter-Namen?
...
@ulipy: tmp ist selten ein sinnvoller Variablenname, und ...
...
MyClass ist ein sehr schlechter Klassenname, warum..
...
Hier zeigt sich gut, wie schwierig sich manchmal Kommunikation über ein so eingeschränktes Medium gestaltet...

Zur Hilfe bei der rein abstrakten Konstruktion einer Klasse habe ich hier viewtopic.php?f=1&t=53566 eine separate Frage gestellt
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@ulipy:kleingeschrieben ist korrekt. Alternativ ließen sich die Werte auch modul-global zunächst an großgeschriebene Bezeichner binden. Lieber aber wäre es mir, wenn Du Dich mit der Funktion und deren Rückgabewert befasst.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

Danke @kbr, ist auf jeden Fall angekommen! Es geht halt nicht alles auf einmal..

Was mir an eine Klasse hier unter anderem sehr gefällt ist z. b. die Möglichkeit, die "versehentliche" Erzeugung eines Attributs durch Verwendung von __slots__ zu verhindern und die klare, einfache Abfrage eines der initialisierten Attribute.

Python lässt dem Programmierer manchmal erschreckend viel Freiheit..
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

ulipy hat geschrieben: Freitag 3. Dezember 2021, 22:40 Python lässt dem Programmierer manchmal erschreckend viel Freiheit..
Genau, viel zu viel Freiheit! Und wenn man nicht bei rot über die Straße zu gehen möchte, muss man seine Füße festnageln.

Anders ausgedrückt: Statt dir Mechanismen zu suchen, die irgendwas verhindern - genügt es nicht, es einfach nicht zu tun? Also statt __slots__ dafür zu missbrauchen, Attribute zu verhindern, sie einfach nicht anzulegen? Der einfache Weg ist in Python immer der richtige, außer in sehr wenigen, sehr speziellen Fällen, bei denen der Programmierer idR. genau weiß, warum er vom einfachen Weg abweicht. Was du dagegen tust, ist Probleme für Lösungen zu suchen.
In specifications, Murphy's Law supersedes Ohm's.
Antworten