Diverse Syntax Problemchen..

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rogerb: Naja, der Zugriff ist bei der Datenklasse schon mal schöner und die Ausgabe des *eigentlichen* Objekts verrät, dass es sich um `Settings` handelt. Auf das `__dict__` greift man nicht direkt zu, das muss ein Objekt ja auch gar nicht zwingend haben.
“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ü

das alles ist bereits einiger "Tobak" für mich, obwohl aus Python-Sicht sicherlich in keiner Weise "komplex".

Mit einer Umsetzung jedenfalls haperts immer noch gewaltig:

Code: Alles auswählen

import os
import json

# del prior to initial testing
os.system('del .settings.json')

# create settings
class Settings:
    """
    hold settings data in class
    could the below be a minimalistic form?
    greater functionality is not required
    """

    settings_dict = []

    def __init__(self):
        # system setting
        # set to zero if call is 'virgin' call
        # is never changed
        self.settings_dict['main_call_count': 0]
        
        # default user settings
        self.settings_dict['del_orphaned': False]   # if true:  del
        self.settings_dict['demo_only': False]        # if true:  demo only
        self.settings_dict['hide_versions': False]   # if true:  hide
        self.settings_dict['symlinks_dirs': False]   # if false: ignore
        self.settings_dict['symlinks_files': False]  # if false: ignore

print(Settings)
print()

print(Settings.settings_dict)

x = Settings
print(x.settings_dict)

# dict is remaining empty "[]" in created class and in instance

x.settings_dict['main_call_count'] = 0
print(x.settings_dict['main_call_count'])
# ERROR ... so gehts also nicht...
Ein über eine Klasse etwas "gesichertes" dictionary wäre schon seeehr fein und ausreichend.

Gelesen und geschrieben würden dann einzelne items des dict eben auf ähnliche Art wie im Code.
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ü

oh, viell. find ichs doch noch moment..
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ü

verzeihung - nee, das dict bleibt ja leer...
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,

worauf möchtest du eigentlich hinaus? Wo läge der Vorteil, wenn man innerhalb der Klasse einen Dictionary anlegt um die Settings zu speichern?
Was spricht gegen einfache Klassenattribute? Was spricht gegen einen simplen alleinstehenden Dictionary?
Machst du es dir vielleicht unnötig kompliziert?
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ulipy: Da steht im Kommentar was von „instance“ — so eine gibt es in dem Code nicht. Du hast mit `x` einfach nur einen weiteren Namen für `Settings` definiert, da wird kein neues Objekt erstellt und auch die `__init__()` deshalb nicht ausgeführt. `anderer_name = name` bedeutet nur das der Wert der an `name` gebunden ist, jetzt halt noch zusätzlich an `anderer_name` gebunden ist, also der selbe Wert über beide Namen erreichbar ist.

Wenn man ein Objekt erstellen würde, dann wäre die `__init__()` hiermit auf die Nase gefallen:

Code: Alles auswählen

  File "forum19.py", line 21, in __init__
    self.settings_dict['main_call_count': 0]
TypeError: slice indices must be integers or None or have an __index__ method
Das wäre eher aufgefallen wenn da nicht schon wieder globaler Zustand drin wäre. Das `settings_dict` gehört nicht in die Klasse sondern in ein Objekt, also in die `__init__()`. Und da wäre dann als nächstes die Frage was *so* einer Klasse dann soll, die einfach nur *ein* Attribut hat und sonst nix. Da werden keine Daten zusammengefasst und auch kein einzelner Wert gekapselt um nach aussen eine andere API zur Verfügung zu stellen. Da ist also nicht wirklich ein Mehrwert über eine Funktion, beziehungsweise in diesem Fall noch nicht einmal das, das wäre einfach eine Konstante.

Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.

Zum löschen von Dateien braucht man keine externen Programme, und es ist sicherer wenn man nicht einfach relativ zum aktuellen Arbeitsverzeichnis etwas löscht. Wer weiss wo das am Ende liegt und was da gelöscht wird.

Code: Alles auswählen

#!/usr/bin/env python3
from pathlib import Path
from pprint import pprint

SETTINGS_FILE_PATH = Path(__file__).parent / ".settings.json"
#
# Delete prior to initial testing.
#
try:
    SETTINGS_FILE_PATH.unlink()
except FileNotFoundError:
    pass

DEFAULT_SETTINGS = {
    # system setting
    # set to zero if call is 'virgin' call
    # is never changed
    "main_call_count": 0,
    # default user settings
    "del_orphaned": False,  # if true:  del
    "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
}


def main():
    pprint(DEFAULT_SETTINGS)
    print(DEFAULT_SETTINGS["main_call_count"])


if __name__ == "__main__":
    main()
“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ü

Tief durchatme... :? Aber es hilft, aller Voraussicht nach.. :)

Es ist tatsächlich so, dass ich "falsch abgebogen" bin bei der Thematik "global or not global", weil selbst die Übergabetechnik(en) mit zu vielen Fragezeichen behaftet war... Der Fokus war danach fehlgeleitet.

Für den Fall Settings genügte dann von der Anwendung her in "bestmöglicher Näherung" ein simples dictionary.

@__blackjack__
der os.system() call war ja in dieser Form ausschließlich für "mündige" Programmierer zur Bequemlichkeit gedacht, die Gefahr jeglicher Anwendung bleibt grundsätzlich auf Seiten des Anwenders :wink:

Das aktuelle "Problem" entsteht erst mit der etwas komplexeren ursprünglich davor geposteten Aufgabe. Teste dazu jetzt erst mal (Beispiel-)Code auf neuer Basis.

Viele Grüß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ü

Hallo @__blackjack__,
möchte zunächst gerne ein paar Sachen, die von deiner Seite erwähnt wurden noch "aufarbeiten":

Namensgebung "settings_dict" für ein dictionary:
War bis jetzt bewusst so gewählt. Der Name soll ein Hindernis für den Programmierer gegen eine unbedachte Änderung des Typs sein. Zudem ist vorgesehen, dass es mindestens bis zu einer neuen [Spass on] "stable" [Spass off] genau ein dictionary bleiben soll.

Instances:
Verstanden, eine Zuweisung erzeugt natürlich erst mal "etwas" identisches, nur unter einem anderen Namen (so meine Ausdrucksweise..) bzw. es erzeugt einen zusätzlichen pointer auf eine identische Adresse.
Krieg aber die Kurve noch nicht:
class myclass:
- erzeugt dann verm. ein "Objekt"
- __init__ weist dem neu erzeugten Objekt ggfs. Inhalte von default-Attributen zu und kann weitere statements ausführen
- das so erzeugte und initialisierte Objekt kann danach im code uneingeschränkt genutzt werden

Habe ich nun eine / 1 Instanz dieses Objekts vorliegen?

Obiges kann sehr "verkehrt" sein - die Terminologie ist noch sehr "vortastend"..

Wie würde denn jetzt eine (weitere) Instanz erzeugt?
Py::: 1. funktional zuverlässig, 2. Anfänger-lesbar, 3. Py-Konformität
Benutzeravatar
snafu
User
Beiträge: 6732
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Weitere Instanzen werden genau so erzeugt wie beim ersten Mal: indem man die Klasse aufruft. Dadurch wird das neue Objekt erzeugt und die __init__-Methode ausgeführt, wie du es ja schon richtig erkannt hast. Warum sollte das ab dem zweiten Mal anders ablaufen...? Was du mit "class" definierst, ist halt der "Bauplan" und deine Objekte / Instanzen sind die Fertighäuser gemäß vorliegendem Plan.
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ulipy: Warum soll das ein `dict` bleiben? Warum darf das kein `types.MappingProxy` sein? Oder was auch immer `shelve.open()` zurück gibt? Warum nicht *irgend etwas* das sich bei Schlüsselzugriffen passend verhält, aber bei einem `isinstance()` für `dict` unwahr ist?

Alles was man einen Namen binden kann ist in Python ein Objekt, also ja die Ausführung von ``class Class:`` erzeugt ein Objekt mit dem Namen `Class`. Das ist aber nicht das Objekt das `__init__()` übergeben wird. `Class` ist ein Objekt welches als ”Bauplan” für Objekte dient, die aus diesem/mit diesem Klassen-Objekt erstellt werden. `Class` beschreibt durch seine Methoden woraus sich diese Objekte zusammensetzen, und wie sie ”reagieren”. Zum Beispiel was passiert wenn man so ein Objekt erstellt, welche Methoden das hat, wie es sich mit bestimmten Sprachkonstrukten verhält, und so weiter. Im Verlauf von diesem Vorgang des „neues Objekt vom Typ `Class` erstellen“ wird die `__init__()` mit einem neuen frischen Objekt aufgerufen (in aller Regel zumindest [1]) und die kann das Objekt dann initialisieren, also in einen brauchbaren Anfangszustand bringen.

*Das* passiert nicht durch die ``class``-Anweisung, sondern in dem man die Klasse aufruft. Also der kleine aber wichtige Schritt, der in Deinem Code gefehlt hat. Und das kann man beliebig oft machen um beliebig viele, unabhängige Objekte zu erstellen. Also beliebig viele im Sinne von, so viel Arbeitsspeicher, beziehungsweise vom Betriebssystem bereitstellbarer Adressraum, vorhanden ist. 🙂

Beispiel:

Code: Alles auswählen

#!/usr/bin/env python3


class Class:
    def __init__(self, value):
        self.attribute = value
        self.another_attribute = 42


def print_instances(instances):
    for instance in instances:
        print(instance, instance.attribute, instance.another_attribute)


def main():
    instance = Class(23)
    another_instance = Class(4711)
    print_instances([instance, another_instance])
    print("-" * 40)
    instance.attribute = 100
    print_instances([instance, another_instance])
    print("-" * 40)
    another_instance.another_attribute = -10
    print_instances([instance, another_instance])


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

<__main__.Class object at 0x7f4e21d576d0> 23 42
<__main__.Class object at 0x7f4e21d57750> 4711 42
----------------------------------------
<__main__.Class object at 0x7f4e21d576d0> 100 42
<__main__.Class object at 0x7f4e21d57750> 4711 42
----------------------------------------
<__main__.Class object at 0x7f4e21d576d0> 100 42
<__main__.Class object at 0x7f4e21d57750> 4711 -10
__________

[1] Die Einschränkung „in aller Regel“, weil vor der `__init__()` die `__new__()`-Methode aufgerufen wird, die dafür verantwortlich ist ein neues, frisches Objekt zu erstellen. Das muss sie aber nicht, sie könnte auch ein bestehendes von irgendwoher holen, und sogar der Typ von dem Objekt muss nicht `Class` sein. Vergiss das aber am besten gleich wieder, denn in 99,99999% der Fälle liefert die `object.__new__()` einfach ein neues frisches Objekt vom passenden Typ.
“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ü

@__blackjack__:
übertreibs nicht {{fingerzeig}} - und zudem lasse ich nix auf mein gutes altes dictionary kommen! 8)
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

ok und danke - das habe ich jetzt wohl einigermaßen verstanden:

Eine Instanzierung erfolgt mit der Zuweisung (zumindest) eines für diese Instanz dann wesentlichen, individuellen Attributs - eigentlich logisch - das muss man halt erst mal verstehen :D

Jedenfalls hat die alles entscheidende 42 dabei ungemein geholfen :D
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das klingt nicht so, als ob du das verstanden hast. Eine Instanzierung erfolgt durch Aufruf des Konstruktors.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

ok, was genau ist im obigen Fall der Konstruktor?
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

Das __init__ (bzw streng genommen __new__, aber das ist hier automatisch gegeben). Das hat __blackjack__ doch alles dargelegt. Also dieser Moment:

Code: Alles auswählen

    instance = Class(23)
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ulipy: Das wäre `__new__()` und die Methode wird aufgerufen wenn man die Klasse aufruft. Und das alleine reicht schon. Eine Zuweisung ist zwar oft sinnvoll, aber nicht notwendig. Also bei ``instance = Class()`` kann man die Zuweisung auch weglassen — ``Class()`` ist der Teil der das neue Objekt erstellt. Mit dem sollte man dann irgend etwas machen. Oft einem Namen zuweisen, direkt oder indirekt. Also indirekt beispielsweise bei einem Aufruf übergeben, Beispiel ``print(Class())`` — da bindet man das ja nicht für sich selbst an einen Namen, aber innerhalb von `print()` wird das Objekt auch irgendwann an einen Namen gebunden werden müssen. Oder man bindet das Objekt selbst nicht an einen Namen, sondern es ist Teil einer literalen Datenstruktur, die dann vielleicht an einen Namen gebunden wird. Beispiel ``instances = [Class(), Class(), Class()]``. Und manchmal erstellt man Objekte auch nur als Teil eines Ausdrucks wo sie dann direkt ”verrechnet” werden, wobei das letztlich nur ein Sonderfall von Übergabe an eine Methode ist, denn letztendlich führen fast alle Operatoren zu Methoden. Beispiel: ``point = origin + Point(23, 42)`` was letztlich auf ``point = origin.__add__(Point(23, 42))`` hinaus läuft.

Hast Du eigentlich schon Programmiererfahrung? Falls ja in welcher Sprache oder Sprachen?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@ulipy,

ich würde mir mal dies durchlesen:
https://docs.python.org/3.9/reference/d ... ject.__new__

Mann kann den Prozess der Instanzerzeugung sichtbar machen indem man die beteiligten Methoden, die normalerweise im Hintergrund ablaufen, überschreibt und damit experimentiert:
Man sieht immer wieder Code-Beispiele in denen die __init__ Methode überschrieben wurde. Man kann die __new__, die vorher aufgerufen wird, aber auch überschreiben.
Dabei muss man aber auf den richtigen Rückgabewert achten, sonst stört man den Ablauf.

Code: Alles auswählen

class Example:
    def __new__(cls, *args, **kwargs):
        """ 
        Die __new__ Methode erzeugt eine Instanz der Testklasse
        Durch Übeschreiben kann man die Funktionalität, die norlmalerweise im Hintergrund abläuft,
        manipulieren:
        """

        print(f"Ist 'cls' eine Instanz der Klasse Test?: {isinstance(cls, Example)}")
        print(cls)
        # print(args)
        # print(kwargs)

        # Hier wird die neue Instanz nach dem Bauplan der Klasse erzeugt
        instance = object.__new__(cls)

        # und muss schließlich zurückgegeben werden um von der __init__ Methode verwendet zu werden
        return instance

    def __init__(self, *args,**kwargs):
        """ Initialisiert die neu erzeugte Instanz"""
        print(f"Ist 'self' eine Instanz der Klasse Test?: {isinstance(self, Example)}")
        print(self)
        # print(args)
        # print(kwargs)


e = Example("arg", b="kwarg")

"""
Ist 'cls' eine Instanz der Klasse Test?: False
<class '__main__.Example'>
Ist 'self' eine Instanz der Klasse Test?: True
<__main__.Example object at 0x000001E719676FA0>
"""
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@ulipy: Der Konstruktor steht in __blackjack__s Fußnote – vergiss den bitte ganz schnell wieder; den brauchst du in Python nur selten und als Anfänger nie. Dich verwirrt vermutlich noch die Trennung von Klassendefinition und Instanziierung. Das kann oft eine Hürde beim Verständnis von OOP sein. Hier definierst du eine Klasse:

Code: Alles auswählen

class TheClass:
    # ab hier dann die Methoden

und hier legst du eine Instanz an:

Code: Alles auswählen

the_instance = TheClass()
Wie du siehst ist die Klasse ein callable. Der Rückgabewert ist eine Instanz. Rufst du die Klasse öfter auf, erhältst du mehrere Instanzen.

Nehmen wir an, die Klasse implementiert einen Warenkorb mit zwei Methoden, 'reintun' und 'rausnehmen', sowie ein Instanz-Attribut mit dem Inhalt (z.B. eine Liste). Dann kannst du mehrere Instanzen anlegen. Bei allen kannst du etwas reintun und auch wieder rausnehmen. Aber der Inhalt kann sehr unterschiedlich sein.
ulipy
User
Beiträge: 83
Registriert: Mittwoch 17. November 2021, 21:42
Wohnort: Ba-Wü

@__deets__:
Da war wieder meine (absolut ungeübte) Terminologie "schuld", welche erneut Zweifel schürte.

hatte das aus dem Beispiel von @__blackjack__ schon so verstanden, jedoch nicht sauber wiedergegeben. der "Konstruktor" war als Begriff bislang wenig genutzt worden

Das Ganze hin und her wäre in einem Kaffee oder Bar natürlich nicht so abgelaufen.

@__blackjack__: zur Einordnung:
Hatte für persönlichen Bedarf durchaus funktionale und fehlerfrei ablaufende kleinere Programme bis zu der Zeit erstellt, als die OOP-Technologie an Fahrt aufnahm.

D. h. wie gesagt, "prozedural" war die Welt für meinen persönlichen oder gewerblichen Bedarf durchaus "in Ordnung", konnte damit ausreichend sicher umgehen.

Ungefähr ab dem Zeitpunkt, an dem OOP aufkam, war ich kaum mehr codierend "am Ball der Entwicklung", schnappte jedoch immer wieder Grundsätzliches daraus auf.

@Alle
Und jetzt hatten sich die Klärungen und Beiträge hier immer wieder "verheddert", weil sowohl die Abstraktionsebenen unter diesen Voraussetzungen durcheinander kamen als auch "geübte" codierer - natürlicherweise - selten "Schulungssprache" verwenden - das ist einfach ein anderer Bahnhof, der nach einiger Zeit bis auf die verbleibenden "wichtigen" Ausnahmen o. ä. wieder "abgelegt" wird.

Und dann nehmen wir einfach noch die auch nach langer Zeit nicht verbesserte :roll: Flexibilität im Aufsaugen neuer Konzepte einschl. deren konkreter Umsetzung dazu, dann haben wir die Suppe, die wir hatten..

Danke für die Geduld!
Der boot-Vorgang hat auf jeden Fall stattgefunden
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ulipy: Gerade wegen den Begrifflichkeiten und Sprachkonstrukten hatte ich nach der bzw. den Sprachen gefragt, damit man vielleicht leichter Gemeinsamkeiten und Unterschiede zwischen Python und OOP und der oder den Sprachen in denen Du schon Erfahrungen gesammelt hast, in vertrauteren Begriffen erklären kann.

Wobei OOP an sich ja schon sehr alt ist. Ich glaube irgendwann Mitte bis Ende der 1960er hat Alan Kay den Begriff geprägt. Aber ich vermute Du meinst den Zeitpunkt als das in den Mainstream kam, in den 1990ern. Viele prozedurale Sprachen von damals wurden ja mit OOP-Konstrukten erweitert. Ich selbst hatte den ersten etwas verwirrenden Kontakt in der Schule mit Turbo Pascal 5.5.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten