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.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Fehler meldet man per Exception an den Aufrufer zurückt, nicht per print und quit.
Jede Funktion sollte genau eine Aufgabe haben, und nicht drei.

Code: Alles auswählen

from pathlib import Path 
import json

SETTINGS_FILENAME = Path('dummy_f_dfg_ff__Y_TmP_mydata.json')

DEFAULTS = {
    'del_orphaned': False,   # if true:  del orphaned versions
    'demo_only': False,      # if true:  demo only
    'hide_versions': False,  # if true:  hide
    'symlinks_dirs': False,  # if false: ignore
    'symlinks_files': False, # if false: ignore
    # 'scan_all_subdirs', # visibility of subdirs for power-user
}


def read_settings():
    if not SETTINGS_FILENAME.exists():
        # read defaults
        return dict(DEFAULTS)
    else:
        # read from file
       return json.loads(SETTINGS_FILENAME.read_text())


def write_settings(data):
    SETTINGS_FILENAME.write_text(json.dumps(data))


def main():
    settings = read_settings()
    print(settings['demo_only'])
    settings['demo_only'] = True
    print(settings['demo_only'])
    write_settings(settings)

if __name__ == "__main__":
    main()
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

Zusätzlich enthält das `if not SETTINGS_FILENAME.exists()` eine TOCTOU-Race-Condition, man sollte besser EAFP benutzen:

Code: Alles auswählen

def read_settings():
    try:
        contents = SETTINGS_FILE.read_text(encoding="utf-8")
    except FileNotFoundError:
        return dict(DEFAULTS)
    else:
        return json.loads(contents)
Es kann auch noch zusätzlich ein `PermissionError` auftreten, wenn die Datei existiert, aber nicht gelesen werden darf.

Außerdem sollte man beim Lesen und Schreiben von Dateien immer ein `encoding` angeben, weil man sonst das Defaultencoding des Systems (unter Windows `cp1252`) benutzt, JSON (und eigentlich alles andere auch) aber immer als UTF-8 kodiert sein sollte.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde die Datei gar nicht selbst lesen oder schreiben, sondern das `json`-Modul das erledigen lassen. Das hat dafür `load()` und `dump()` (ohne `s`), und wenn man die Datei im Binärmodus öffnet, kümmert es sich auch gleich selbst um die Kodierung. Beim schreiben UTF-8 und beim lesen erkennt es beliebige UTF-* am BOM falls vorhanden, oder eben UTF-8 falls nicht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und das else kann bei den Codes der Vorposter auch entfallen, da sich in den Fällen der Programmfluss nicht ändern würde.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

Danke für die hilfreichen Komentare!
Hier die für mich wichtigsten Stichwörter:

Nur zwei *args?
ja, die größten "Kopfschmerzen" bzw. "Bauchweh" lag auch auf meiner Seite bei der Verwendung von nur zwei *args!
Zuerst waren das drei, dann zwei, jetzt sind es wieder drei.
Das Motto "weniger ist besser" war ganz klar über das Ziel hinausgeschossen...

Nur eine Funktion / ein Objekt?
Ja, dabei wird es wohl bis auf Weiteres bleiben, vermute ich.
Der in diesem Fall m. E. letztlich überwiegende Vorteil ist der "Schutz" gegen Vergessslichkeit, "Schlamperei", Sucherei etc.

Lesbarkeit der Verzeweigungen:
Ja, durch die Verwendung von drei args ist das jetzt VIEL "schöner" umzusetzen, in mehrfacher Hinsicht ....

@__blackjack__
das Thema codecs ist leider ein SEHR tiefes, wie so viele andere auch...
Ich bin ausgegangen davon, dass Python3 (liegt heirr zu Grunde) utf-8 als default verwendet.
Auf die Möglichkeit, dass externe Module (hier json) dies anders sehen könnten, war ich bisher nicht gekommen :o - dies könnte jedoch der Fall sein - keine Ahnung..
An die Runde:
Wie verhält sich denn dieses?

Falls json diesen Python3-default berücksichtigt, wird für jeden vorkommenden Fall automatisch utf-8 verwendet - falls nicht, ist dieser natürlich in jedem Fall anzugeben :!:

So sieht das jetzt aus:

Code: Alles auswählen

import os
import json

# Meta-Bemerkungen (in deutscher Sprache) entfallen ab dem pre-release.
# Sie dienen bis dahin lediglich Kommentaren, welche zum leichteren Verständnis von "Merkwürdigkeiten" im Code dienen

# !access settings via this function only please!
def rw_settings(*args):
    """
    Read / write settings
    If no settings-file is found, use defaults
    
    *args:
      accepted are from 1 to 3 arguments

      args[0]:
        is defining the access-mode

        valid args[0]:
        - 'read_default_data'
          NO write operation is permitted

        - 'ro' (read only)
          return a dict-value
          EXCEPTION:
          in case no file for settings-data is found on disc,
          defaults initially are written to file
        
        - 'rw' (read write)
          case a, no (optional) third arg is available:
            return a value
          case b, a third arg is available:
            update a key-value and
            return this value
      
      args[1]:
        - represents a key only
      
      args[2]:
        - represents a value only
      
    Access samples:
    # sind hier noch veeeeeerkehrt angegeben .........................
    
    Read:
      rw_settings('ro', read_default_data')
      rw_settings('_symlinks_dirs')
    
    Write:
      rw_settings('_symlinks_dirs', 'True')
    """

    # no. of arguments: two or three only
    if 1 < len(args) > 3:
        
        # Implementation des error-handling ist temporär !!!

        print('!ERROR!   !ERROR!   !ERROR!')
        print('expected 1 or 2 args only, received:', len(args))
        quit()

    # Angabe des Arbeitsverzeichnisses und des Dateinamens ist temporär !!!
    # set path for settings-file
    data_path = '.\\dummy_f_dfg_ff__Y_TmP_mydata.json'

    # defaults for settings data
    data = {
        '_del_orphaned': False,   # if true:  del orphaned versions
        '_demo_only': False,      # if true:  demo only
        '_hide_versions': False,  # if true:  hide
        '_symlinks_dirs': False,  # if false: ignore
        '_symlinks_files': False, # if false: ignore
        # 'scan_all_subdirs', # visibility of subdirs for power-user
        }

    if args[0] == 'read_default_data':
        # return default dict
        return data

    elif args[0] == 'ro':
        # EXCEPTION:
        # in case no file is existing, create one, using default data
        if not os.path.exists(data_path):
            with open(data_path, 'w') as f:
               json.dump(data, f)

        # read settings from file
        with open(data_path, 'r') as f:
            data = json.load(f)

        return data[args[1]]

    elif args[0] == 'rw':
        # a third arg is expected
        # update key-value
        data[args[1]] = args[2]

        # write updated data to disc
        with open(data_path, 'w') as f:
           json.dump(data, f)

        return data[args[1]]

    else:
        print('!ERROR!   !ERROR!   !ERROR!')
        print('unknown arg received:', args[0])
        quit()

print("rw_settings('read_default_data'):")
print(rw_settings('read_default_data'))

print("rw_settings('ro', '_demo_only'):")
print(rw_settings('ro', '_demo_only'))

print("rw_settings('rw', '_demo_only', 'True'):")
print(rw_settings('rw', '_demo_only', 'True'))
Könnte das nach den Änderungen erst mal so stehen bleiben?
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Nee, weil es schrecklich ist.
Es ignoriert halt alles, was zu deinem Code gesagt wurde. Sich mit diesem Thema zu beschäftigen ist also reine Zeitverschwendung.
Natürlich kannst du gegen die Progammiersprache programmieren und irgendwie außergewöhnlich schwer verständlichen Coden schreiben. Es ist dann halt aber außergewöhnlich schwer verständlicher Code und hat mit dem, was Python aus macht, nichts zu tun.

Eine gute Lösung für dein Problem wurde dir her im Thread bereits gezeigt. Ich sehe nicht, wo dein Code dem überlegen sein sollte. Ich sehe aber viele Argumente dagegen. Und auch die wurden hier schon alle benannt.
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Also nach seinem letzten Beitrag hat er sich für mich stark trollverdächtig gemacht. Aber wer weiß. Hier tummeln sich ja öfter mal so schräge Vogel rum, die tatsächlich ernst meinen, was sie schreiben.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

Vorneweg:
an die TOCTOU-Racecondition z. B. hatte ich zu dem Zeitpunkt einfach nicht gedacht

Wenn das hier so rauskommt tut mir das leid - "schräger Vogel" wäre dann im Vergleich die eher treffende Einschätzung.

Was mir das zeigt:
Die Energie, welche erforderlich wäre, "pythonische" Regeln und Stil einzuhalten, wird keine meiner Optionen sein. Das ist auf einer Ebene zwar schade - die Welt geht dadurch jedoch nicht unter. Wichtig ist, dass mir das klar bewusst ist.

Was folgt daraus?
Python als Werkzeug zur Umsetzung funktionaler anwendungssicherer Programme zu verwenden.

Das "Problem" Lesbarkeit hier auf dieser Ebene stammt mit Sicherheit aus dem Unterschied in der grundsätzlichen "Denke". Wenn jemand - wie praktisch alle hier - mit OOP "aufgewachsen" ist, erscheint die Lesbarkeit "schwierig". Im umgekehrten Fall, wenn jemand OOP nie gelernt oder angewandt hat, kann er mit dem Code hier einigermaßen sicher umgehen.

Ich bin/war(?) tatsächlich dankbar für die vielen Hilfen!
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

ulipy hat geschrieben: Donnerstag 9. Dezember 2021, 18:25Das "Problem" Lesbarkeit hier auf dieser Ebene stammt mit Sicherheit aus dem Unterschied in der grundsätzlichen "Denke". Wenn jemand - wie praktisch alle hier - mit OOP "aufgewachsen" ist, erscheint die Lesbarkeit "schwierig". Im umgekehrten Fall, wenn jemand OOP nie gelernt oder angewandt hat, kann er mit dem Code hier einigermaßen sicher umgehen.
Nö. Die Einschätzung ist falsch.
Mit Objektorientierung hat dein Code hier nichts zu tun.
Du versuchst hier Erklärungen für schlechten Stil zu finden - da ist aber keine.
Ich wiederhole mich: Wie es richtig, gut und simpel geht wurde hier ausreichend dargelegt. Ganz ohne Objektorientierung.
Was mir fehlt ist deine Erklärung warum deine Lösung mit dem magischen ersten Argument besser sein soll als korrekt benannte Funktionen.

Es sollte selbsterklärend sein, dass dem Leser (und das bist auch du bei deinem eigenen Code)

Code: Alles auswählen

def write_settings(...
eingängiger ist als

Code: Alles auswählen

def read_or_write_settings("write", ...
Eine Funktion hat eine Aufgabe. Nicht mehrere. Auch das wurde hier bereits gesagt. Es heißt übrigens "Funktion" wie in "funktionale Programmierung" nicht wie "Funktion" in "Objektorientierte Programmierung".

Als ich von Zeitverschwendung gesprochen habe, meinte ich übrigens eher die Leute, deren Beiträge zu deinem Code du hier ignorierst - nicht dicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ulipy: Ich glaube nicht das hier alle mit OOP ”aufgewachsen” sind. Ich zum Beispiel nicht. Und bei der letzten Fragestellung mit der Funktion die mehrere Sachen macht und als Schnittstelle dann ein total undurchsichtiges ``*args`` mit zwei oder drei Argumenten bekommt, hat die Kritik ja auch gar nichts mit OOP zu tun. Das eine Funktion in der Regel genau *eine* Sachen machen sollte und nicht in der Funktion mehrere, sich ausschliessende Pfade existieren sollten, ist etwas das vor 30 Jahren als ich in Pascal, QBasic, und C programmiert habe, schon galt. Da gab es manchmal Gründe Ausnahmen zu machen, die es heute nicht mehr gibt, weil Laufzeit und Speicherverbrauch heute weniger kritisch sind und man beispielsweise nicht mehr darauf achten muss, dass ein Codesegment in 64 KiB passen muss, oder es auch noch Rechner gibt die im einstelligen Mhz-Bereich getaktet sind.

Ausserdem ist OOP ein Paradigma und nicht eine Spracheigenschaft und wenn man OOP gelernt hat, wird man sicher auch APIs in prozeduralen Programmiersprachen ”finden”, die sich sehr objektorientiert anfühlen. Der Schritt in Pascal von RECORD + Prozeduren/Funktion und gelegentlichen Prozedur- und Funktionstypen zu OBJECT und Methoden ist auch kein wirklich grosser. Und diesen Schritt konnte man ja auch vor 30 Jahren schon gehen. Ich hatte den damals im ersten Anlauf nicht richtig verstanden, unter anderem weil ich den entscheidenden Unterschied nicht sah und das erst nur für einen reinen syntaktischen Unterschied wahrnahm eigentlich das gleich zu schreiben, was ich sowieso schon hatte. So richtig Klick gemacht hatte das dann erst Ende der 90er mit Java.
„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ü

@sparrow
...
Was mir fehlt ist deine Erklärung warum deine Lösung mit dem magischen ersten Argument besser sein soll als korrekt benannte Funktionen.
...
Ok, das ist mit Sicherheit überwiegend, jedoch nicht ausschließlich, einem minimalistischen Ansatz zu verdanken. Dies kann ohne Übertreibung als "Eigenart" meinerseits gesehen werden.

Die gewählte Lösung wird dadurch selbstverständlich in keiner Weise "besser", verglichen mit "guten" Standards.

(Dazu eine weiter Anmerkung in der Antwort auf @__blackjack__)
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__
@ulipy: Ich glaube nicht das hier alle mit OOP ”aufgewachsen” sind. Ich zum Beispiel nicht.
ja, das ist als bekannt vorauszusetzen, ebenso OOP als Paradigma (nicht als besondere "Eigenschaft" einer bestimmten Sprache).

Am Ende steht hier vor allem diese Geschichte:
"eine" anstelle von "zwei" Funktionen:
Es ist für mich auch beim allerbesten Willen schlicht unmöglich, in diesem Falle zwei Funktionen zu verwenden, wenn ich durch eine vielleicht etwas "eigene" Art erreiche, NIEMALS NICHT mehr über "settings" nachdenken zu müssen - in keiner Form, zu keinem Zeitpunkt, so lange nicht, bis sich etwas grundlegendes ändert.

Die Verwendung der *args hier erscheint mir persönlich dabei nun (in der aktuellen Form) tatsächlich nicht schwierig nachzuvollziehen, gleich, mit welcher persönlichen Geschichte man da herangeht.
Wenn jemand den docstring liest, hat er eigentlich alles, meine ich jedenfalls (ich kann damit natürlich falsch liegen - das würde dann jedoch entsprechende konkrete Argumente benötigen, was genau daran eigentlich "schwierig" ist. Solche kann ich hier bis heute jedenfalls nicht sehen.)

Alleine die Befolgung von Standards, hier, in diesem Falle, wird - für mich zumindest - kein Grund für einen Ansatz mit zwei Funktionen sein können.

So ticke ich halt..
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was ist daran minimalistisch? Es ist doch undurchschaubar und komplex. Statt zwei Funktionen eine zu bauen, die dann innen drin wieder per if unterscheiden, worum es geht, ist nicht weniger. Es ist nur weniger strukturiert 🤷‍♂️
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

ulipy hat geschrieben: Donnerstag 9. Dezember 2021, 20:16Alleine die Befolgung von Standards, hier, in diesem Falle, wird - für mich zumindest - kein Grund für einen Ansatz mit zwei Funktionen sein können.

So ticke ich halt..
Dann brauchst du aber kein Forum.
Dann brauchst du einen einsamen Platz, wo du in deinem eigenen Saft schmoren möchtest..
Offensichtlich möchtest du, dass dir hier irgendw jemand sagt, dass dein Code in Ordnung ist. Ist er nicht. Du wirst dafür von keinem hier Absolution erhalten.
Deine Argumente, warum der Code so sein sollte sind augenscheinlich falsch (das wird einem spätestens klar, wenn die vorgeschlagene Lösung und deine nebeneinander liegen - dann ist sehr offensichtlich, welche von beiden minimalistisch ist).

Da du also keine nachvollziehbaren Argumente bringen kannst und alle Tipps ignorierst bin ich mal raus.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@ulipy: Wenn der Code für dich so gut verständlich ist, wo ist das Problem?

Insbesondere nach den etlichen Iterationen hier im Thread sollte der Code also nicht nur richtig funktionieren, sondern auch gut getestet sein. Wenn da jetzt also immer noch ein Fehler drin wäre... würde das dann nicht zeigen, dass der Code nicht gut verständlich bzw. zu komplex ist? Zum Beispiel weil ein Randfall übersehen wurde, der nicht auftreten kann, wenn du den Code in sinnvolle Funktionen aufteilst?
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

__deets__ hat geschrieben: Donnerstag 9. Dezember 2021, 20:18 Was ist daran minimalistisch? Es ist doch undurchschaubar und komplex. ... ... Es ist nur weniger strukturiert 🤷‍♂️
Ok, es ist ohne Frage weniger strukturiert - undurchschaubar würde ich es jetzt nicht nennen können - jeder hier hat das durchschaut, so weit ich das mitbekommen habe :wink:
sparrow hat geschrieben: Donnerstag 9. Dezember 2021, 20:26
ulipy hat geschrieben: Donnerstag 9. Dezember 2021, 20:16Alleine die Befolgung von Standards, hier, in diesem Falle, wird - für mich zumindest - kein Grund für einen Ansatz mit zwei Funktionen sein können.

So ticke ich halt..
Dann brauchst du aber kein Forum.
...
Da du also keine nachvollziehbaren Argumente bringen kannst und alle Tipps ignorierst bin ich mal raus.
Wenn du das so siehst - ich kann dich nicht daran hindern. "Keine" Argumente gebracht und "alle" Tipps ignieriert habe ich jedenfalls m. W. nicht.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es dauert eben länger, bis man etwas versteht. Das liegt an mangelnder Struktur und schlechtem Design. Das man es trotzdem verstehen *kann* ist kein Ausweis dafür, das es gut ist.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

narpfel hat geschrieben: Donnerstag 9. Dezember 2021, 20:27 @ulipy: Wenn der Code für dich so gut verständlich ist, wo ist das Problem?
...
Hatte deinen Einwurf nicht rechtzeitig gesehen.

Bin mir fast sicher, dass dir selbst klar ist, dass die Schlussfolgerung(en) logisch gesehen so nicht wirklich "legitim" sind - jedenfalls gibt es mit Sicherheit noch viele "Problemchen..", die dann eben auch speziell mit Python Syntax etc. zu tun haben.

Hätte ich die "42" richtig gut verstanden, wäre dies natürlich obsolet :)
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@ulipy: Ich bin mir sicher, dass du deine Funktion nie mit null Argumenten getestet hast, aber (fehlerhaften) Code geschrieben hast, der das behandeln soll. Dass dir das nicht aufgefallen ist, zeigt, dass der Code zu komplex ist. Insbesondere auch, weil die Prüfung komplett überflüssig wäre, wenn du den Code auf zwei Funktionen aufgeteilt hättest.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

__deets__ hat geschrieben: Donnerstag 9. Dezember 2021, 20:54 Es dauert eben länger, bis man etwas versteht. Das liegt an mangelnder Struktur und schlechtem Design. Das man es trotzdem verstehen *kann* ist kein Ausweis dafür, das es gut ist.
Ja, ok, als "Ausweis" für eine "gute" oder gar vorzuziehende Herangehensweise hatte ich dies m. W. unmissverständlich weder gesagt oder dargestellt, denke ich zumindest - gemeint sowieso nicht
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Antworten