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ü

narpfel hat geschrieben: Donnerstag 9. Dezember 2021, 21:23 @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.
Absolut einverstanden - danke!
Es fand bisher kein nennenswertes Testen statt und ich war und bin absolut nicht der Ansicht, dass der Code - so wie er gerade ist - bereits zuverlässig verwendbar wäre.

Im Gegenteil - es war klar, dass das rein logische Nachvollziehen hier nicht ausreichernd ist, genau wegen dieser Struktur..
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ü

Nachtrag: ich gehe aber davon aus, dass der Programmierer rasch etwas bemerken würde, falls er sich so etwas einfallen ließe...
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ü

@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.
Jetzt muss ich doch sofort nachfragen - das ist erst mal sehr überraschend:
Weshalb genau ergibt dies einen Laufzeitfehler - ein solcher is ja deutlich unwillkommener als eine Parser-Meldung...

Mit
if 1 < len(args) > 3 or len(args) == 0:
passiert das nicht mehr :o
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit positions Argumenten hätte man lesbare Namen, und sowas passiert erst recht nicht, weil der Interpreter einen warnt. Ohne das man das vergessen kann & den Code mit solchen statements versalzt.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@ulipy: Für welche Zahlen `x` gilt denn `1 < x > 3`?
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@all
in dem für mich als crash-Seitenensteiger extrem "dichten Wald" "Python" war ich in Sachen if-Struktur auch noch betriebsblind geworden :shock:

[ironie an]
Hätte mir doch jemand erklärt, dass "man" eine read-only Abfrage auch in einen bereits vorhandenen read-only block mit reinnimmt, aber nein...
[ironie aus]

Wurde dann heute beim Lesen von viewtopic.php?f=1&t=53600 wenigstens etwas getröstet - ich bin nicht alleine :wink: (wobei dies thematisch mit dem vorliegenden nichts bis wenig zu tun hat, mir jedoch sehr bekannt vorkommt...)

Vielleicht ist das hier unten ein besserer Weg, um erst daran anschliessend ggfs. auftauchende Umsetzungs-Schwierigkeiten klären zu können?

Die Erstellung eines doc-strings vor dem Umsetzen hätte ganz klar viele Vorteile - zumindest für mich persönlich (bin da ja aus Erfahrung nun durchaus deutlich vorsichtiger geworden..)
Beim ursprünglichen Wunsch, eine kleine Hilfe für den reinen Eigenbedarf zu erstellen, kam diese Ebene - wie fast immer, denke ich - definitiv zu kurz :!:

Meint ihr, so was macht Sinn?

Code: Alles auswählen

# Meta-Bemerkungen (in deutscher Sprache) entfallen ab dem pre-release.
# Sie dienen bis dahin ausschließlich Kommentaren, welche zum leichteren Verständnis des aktuellen Code-Status dienen

# !access settings via this function only please!
def settings(mode, keywrd, value):
    """
    Requires:
      # hier stehen momentan nur zwei Platzhalter
      environement(...)
      custom_error_handling(...)

    At program start, data_dict is loaded either 
    from default_dict or from a settings-file
    
    Arguments use:

      mode                  keywrd        value
      ----                  ------        -----
      'read_default_data'   None          None
        return: data_dict

      'save_settings_dict'  None          None
        return: data_dict

      'get_keyval'          dict-key      None
        return: value

      'update_keyval'       dict-key      value
        return: value

    Access samples:
      settings('read_default_data', None, None)
      settings('save_settings_dict', None, None)
      settings('get_keyval', key, None)
      settings('update_value', key, value)
    """
Falls ja, könnte zuerst die Logik im doc-string abgeklopft werden?

Zumindest kann ich mir das vorstellen.

Die Umsetzung könnte dann bei Bedarf am Ende dessen stehen.
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Dass jetzt kein *args mehr benutzt wird, ist definitiv besser. Trotzdem, statt eine Funktion zu schreiben, die über ihr erstes Argument vier verschieden Dinge macht, ist es immer noch besser vier Funktionen zu schreiben.
Das `Requires` ist unnötig, oder hast Du das schon jemals in einem Doc-String gelesen? Der Entwickler der Funktion muß ja sicherstellen, dass alle Abhängigkeiten vorhanden sind, das interessiert den Nutzer nicht die Bohne.

Und was zum Henker hast Du Dir dabei gedacht, bei keywrd das o wegzulassen? Wir sind nicht bei Glücksrad, wo man Vokale extra kaufen muß.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@narpfel
so wie du fragst, ist mit Sicherheit der Wurm drin - der Test 1 < len(args) > 3 war auf genehmigte 1 bis 3 Argumente angelegt (falsch natürlich wegen der 1, hätte 0 sein sollen...)

@Sirius3
Klare Ansage!
- Sparen ist nicht immer richtig - war absolut unnötig
- das mit der einen Funktion ist "eigen"
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: Wie ist es denn mit 0 statt 1 richtiger?
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@narpfel
war nicht das wesentliche Problem zu dieser Zeit - weshalb sagst du mir nicht einfach, wie die korrekte Syntax 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ü

@Sirius3
Hmmm.. - den Docstring hatte ich bisher ausschließlich als für den Entwickler gedacht gesehen - nciht für den Anwendere des Programms. Verhält sich dies üblicherweise anders?
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: Die Syntax ist richtig, aber die Bedingung ist falsch. Das hier:

Code: Alles auswählen

1 < len(args) > 3
bedeutet dasselbe wie

Code: Alles auswählen

1 < len(args) and len(args) > 3
und damit dasselbe wie

Code: Alles auswählen

1 < len(args) and 3 < len(args)
Die Bedingung ist also bereits erfüllt, wenn len(args) größer ist als 3. Der Test darauf, ob len(args) größer ist als 1, ist folglich redundant, weil alles, was größer ist als 3 automatisch auch größer ist als 1.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@ulipy: die Dokumentation enthält normalerweise keine Implementierungsdetails. Die muß der Anwender der Funktion nicht kennen.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

pillmuncher hat geschrieben: Samstag 11. Dezember 2021, 20:08 @ulipy: Die Syntax ist richtig, aber die Bedingung ist falsch.
...
Danke dir, schau mir das genauer an!

@Sirius3
oh mei - wann würde denn der Docstring für einen Anwender relevant? Die Idee war ja genau die, ihn nur sehr begrenzt mit eher technischen Details zu konfrontieren -- auch noch auf englisch .
Dachte bis jetzt, der Anwender kommt nicht in den Kontakt mit Docstrings?
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: Der Anwender einer Bibliotheksfunktion ist der Programmierer, der diese Bibliotheksfunktion verwendet. Der Docstring ist für diesen Programmierer gedacht.
In specifications, Murphy's Law supersedes Ohm's.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

ok, dann habe ich das jetzt so verstanden:
der Anwender (Programmierer) muss in der Regel keine Implementierungsdetails aus dem docstring entnehmen können - stimmt das denn dann so?
Die Abgrenzung wäre damit natürlich Ermessensfrage
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ü

schaue jetzt erst mal im PEP 257 nach - komme dann evtl. zurück...
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ü

@pillmuncher, @narpfel
Nach einem "Klassenbesten" in Mathe es ist eine (fast) erschreckende Selbsterkenntnis, eine schlichte logische Bedingung dermaßen "falsch" zu formulieren.
Es sind die knapp dreißig Jahre, in denen dieser "trait" nicht bewusst und diszipliniert genutzt wurde, die solche Aberrationen erlauben...

Für meinen Teil ist das hier erst mal durch - ich stell nach den Anpassungen die ganze Funktion nochmals rein.

Danke!
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ü

@all

Hab jetzt versucht, so eigentlich alles bisher Gesagte zu berücksichtigen - außer der gewollt beibehaltenen "Eigenheit" der Bedeutung des nun "task" genannten ersten args.

Bin inzwischen wirklich müde mit den "settings" - aber leider hilft das nicht weiter... es sollen ja die wichtlgsten Aspekte pythonischen (einfachen) Codes berücksichtigt sein, damit diese dann auch in kommenden Modulen angewandt werden können.

Wenn's "zerrissen" werden muss, dann muss das eben so sein...

Code: Alles auswählen

### B I T T E  V O R S I C H T ###
# Es könnte zu Testzwecken eine Testdatei in das
# Arbeitsverzeichnis geschrieben werden!
# Der Dateiname ist jedoch so gewählt, dass ein
# Konflikt zu unser aller Lebzeiten - auch in Summe -
# eher nicht vorkommen kann. :-)

# Meta-Bemerkungen (in deutscher Sprache):
#  Diese entfallen ab dem pre-release
#  Sie dienen bis dahin ausschließlich Kommentaren,
#  welche dem leichteren Verständnis des aktuellen
#  Code-Status während der Entwicklung dienen

# Laufzeitumgebung (Planungsgrundlage):
#   Win10, Python3
#   Umgebungsvariablen wie z. B. Installationsverzeichnis,
#   Zugriffsrechte etc. werden an anderem Ort überprüft

# BS-Unabhängigkeit:
#   Betriebssystem-Unabhängigkeit hat ! Erste Priorität !
#   (Sofern für das aktuelle target-System begründet
#   keine alternativen, proprietären Optionen vorzuziehen sind

import os
import json

# define customError class to intercept possible
# errors during coding of the APP
class customError(Exception):
    pass

# !access settings dictionary via this function only please!
def settings(task, keyword, value):
    u"""
    Read write settings data.
    
    At program start, settings_data is loaded either 
    from DEFAULT_SETTINGS or from data_path (settings-file).
    
    The arguments use is self-explanatory:

      task                  keyword        value
      ----                  ------        -----
      'read_default_data'   None          None
        return: settings_data

      'save_settings_data'  None          None
        return: settings_data

      'get_keyval'          dict-key      None
        return: value

      'update_keyval'       dict-key      value
        return: value

    Access samples:
      settings('read_default_data', None, None)
      settings('save_settings_data', None, None)
      settings('get_keyval', key, None)
      settings('update_keyval', key, value)

      # raise a customError:
      settings('read_default_data', None, 'any item except "None"')
    """

    ERR_MSG_ARG   = 'An arg != None was encountered unexpectedly'
    ERR_MSG_ARGS  = 'The type of at least one of two arguments was detected to be != None'
    ERR_MSG_TYPE  = 'Arg one unexpectedly was not of type string'
    ERR_MSG_TASK  = 'Arg one was not interpretable'

    # Arbeitsverzeichnis und Dateiname ist temporär !!!
    data_path = '.\\dummy_f_dfg_ff__Y_TmP_mydata.json'

    # defaults for settings data
    DEFAULT_SETTINGS = {
      '_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
    }

    # load settings_data
    if not os.path.exists(data_path):
        settings_data = DEFAULT_SETTINGS
    else:
        with open(data_path, 'r') as f:
            settings_data = json.load(f)    

    # get DEFAULT_SETTINGS
    if task == 'read_default_data':
        if keyword != None or value != None:
            raise customError(ERR_MSG_ARGS)
            quit()

        return DEFAULT_SETTINGS

    # save settings to disc
    elif task == 'save_settings_data':
        if keyword != None or value != None:
            raise customError(ERR_MSG_ARGS)
            quit()

        with open(data_path, 'w') as f:
           json.dump(settings_data, f)

        return settings_data

    # get a settings value
    elif task == 'get_keyval':
        # if not type(value) == str:
            # raise customError(ERR_MSG_TYPE)
            # quit()

        return settings_data[keyword]

    # update a dict value and save it
    elif task == 'update_keyval':
        # if not type(value) == str:
            # raise customError(ERR_MSG_TYPE)
            # quit()

        settings_data['keyword'] = value
        with open(data_path, 'w') as f:
           json.dump(settings_data, f)

        return settings_data[keyword]

    else:
        raise customError(ERR_MSG_TASK)
        quit()

print("settings('read_default_data', None, None)")
print(settings('read_default_data', None, None))
print()

print("settings('save_settings_data', None, None)")
settings('save_settings_data', None, None)

settings('get_keyval', '_hide_versions', None)
print("settings('get_keyval', '_hide_versions', None)")

settings('update_keyval', '_hide_versions', True)
print("settings('update_keyval', '_hide_versions', True)")

# raise a customError:
# print("settings('read_default_data', None, 'any item except None'")
# print(settings('read_default_data', None, 'any item except None'))
Das oben Stehende läuft wenigstens ohne Fehlermelduung durch - so weit ist das dann getestet...
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Total intuitiv, diese festgelegten Strings und das doppelte None. Richtig pythonisch und bestimmt auch sehr leicht zu debuggen, wenn irgendwo ein Buchstabe verdreht oder vergessen wurde. 😍
Antworten