Programm soll seine Bedienung aufzeichnen & wieder abspielen können

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.
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

Grüezi mitenand! :-)
Als "Youngs­ter" (Ü60) ;-), wage ich mich in dieses Forum, um meine Python-Ideen und die daraus entstehenden "Knorze" zur Diskussion zu stellen und gemeinsam eine feine Lösung zu finden. Ganz nach dem Motto: "Gemeinsam geht's besser!"

Ich kenn die Möglichkeit aus der Fotobearbeitung, da kann ich meine Änderungsschritte anzeigen lassen, soviele Schritte zurückgehen, wie ich will. Diese Schrittfolge auch als Makro ablegen und automatisch ausführen lassen.
Ich suche somit Vorgehen, wie ich meine Programme schreiben kann (mit Dekoratoren?), dass das Programm die Bedienschritte aufzeichnet (in eine Liste schreibt?).
Wenn ich diese Liste dem Programm beim Start mitgebe, dann soll es zum selben Ergebnis kommen, wie zuvor von Hand (bei gleicher Startausgangslage).
Von dieser Stelle kann ich dann von Hand weitermachen. ...

Spontan habe ich an eine Liste von Tupeln gedacht, welche den Befehl und die notwendigen Parameter enthalten.
Die Liste wird durch einen Dekorator gefüllt.
Abgearbeitet wird die Liste in einer Schlaufe, welche mittels BefehlsDictionary[<Befehl>](<Parameter>) so die gewünschten Aktionen ausführt.

Erste Versuche mit Funtionen verliefen erfreulich. :-) Mit Klassen, da komme ich ins Schleudern! :-(

Nun bin ich entsprechend verunsichert:
- will ich etwas umsetzen, was auf einem anderen Weg schon gelöst ist?
- bin auf dem richtigen Weg?
- ich habe mit meinen Stichworten keine Hinweise im Internet gefunden. Welche Stichworte sollte ich verwenden? (ich will kein Logging)

Vielen Dank für Eure Hilfe, Hinweise, Tipps, Vorschläge, ...

Jörg
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

An sich klingen deine Ansätze durchaus richtig. Der Begriff der da gerne genutzt wird ist „command pattern“. Damit werden Programme mit undo und redo ausgestattet. Was ja deine Anforderung zu erfüllen scheint.

Klassen machen da Probleme, weil ein Objekt erstmal keine natürliche Repräsentation hat, die es beim redo später identifiziert. Ein Weg um das zu tun besteht darin, automatische IDs zu vergeben. Wichtig ist dabei dann unter anderem, die Erzeugung einer Instanz zu protokollieren. Und dadurch können dann spätere Operationen aus dem self Argument die betroffene Id ermitteln, und das redo kann funktionieren.

Ob es dafür fertige Ansätze gibt, Weiß ich nicht. Bin da aber skeptisch. Sowas ist so verwoben mit der eigentlichen Aufgabe des Programms, das es schwer sinnvoll abstrahierbar ist.

Ein alternativer Weg ist die Nutzung eines reinen read only Daten Modells, dessen verschiedenen Iterationen man einfach alle speichert. Das Programm selbst ist dann ein sogenannter „purer“ view, also nur eine oder mehrere Funktionen, die eine Darstellung berechnen.

Aus Effizienzgründen sollte man die Daten so implementieren, das ein Stand aus dem andern nur durch eine Differenz berechenbar ist. Das komprimiert die Daten gehörig, und macht das so erst möglich. Mein Kumpel Juan hat das für C++ mit der Bibliothek „immer“ umgesetzt. Siehe https://github.com/arximboldi/immer

Ggf finden sich da weiterführende Ansätze auch für Python.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@JoePy: Den zweiten Vorschlag von __deets__ könnte man vermutlich mit Persistenten Datenstrukturen lösen, zB. https://github.com/tobgu/pyrsistent.
Das ist mir vor ein paar Tagen untergekommen, ich habe aber noch nicht damit rumgespielt, deswegen kann ich auch nicht sagen, ob es was taugt.
In specifications, Murphy's Law supersedes Ohm's.
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

@__deets__ & pillmuncher
Vielen Dank für Eure Antworten!
Die Stichworte helfen mir sehr, bessere Suchergebnisse zu finden und dort zu erfahren, wie mein Vorhaben schon angegangen worden ist.
(In PyPI habe ich ein Modul gefunden, das in meine Richtung zielt. Ich möchte aber nicht bei jeder Fkt/Instanz spezielle Funktionen einbauen, das schwebt mir mit der Dekorator-Möglichkeit eine universellere Lösung vor.)

Beim zweiten Vorschlag, stimme ich voll zu. Das ist auch meine Absicht, bei Programmunterbruch die Daten zu speichern und beim Neustart diese zu laden und dort weiterzumachen.
Die "Skriptfähigkeit" will ich verwenden können, wenn ich andere Startdaten habe, aber weiss, dass ein schon gespeicherter Bedienablauf auch darauf angewendet werden kann. Ich erspare mir also eine manuelle Bearbeitung.
"Persistente Datenstrukturen", da muss ich mich zuerst schlau machen, ist für mich ein Fachbegriff, den ich (noch) nicht kenne und einzuordnen weiss.

Ich habe weiter gepröbelt und eine simple Lösung (get_name) gefunden, um den "Namen" einer Instanz herauszufinden (die Instanz sucht ihren Self-Wert in globals). Diese wollte ich in die __init_-Struktur der Klasse einbauen, sodass beim Erzeugen einer Instanz automatisch der Name gesucht und als Attribut abgelegt wird. Musste leider feststellen, dass globals() beim Initieren den betreffenden Eintrag noch nicht kennt. :-(
Rufe ich gleich nach Erzeugen der Instanz, <Instanz>.get_name() auf, funktioniert es wunderbar. Aber eben, ich muss NACH Erzeugen nochmals speziell..., das gefällt mir nicht.
Seht Ihr eine Möglichkeit innerhalb der __init__-Methode einem Attribut (ich verwende "frech" __name__, wird ja bei Class-Instanzen nicht verwendet. Könnte dies Seiteneffekte provozieren?) den Namen zuzuordnen?
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es tut mir leid, aber das ist etwas sehr unverstaendlich formuliert alles. Was ist "nochmals speziell ..."? Und was genau soll/muss da fuer ein Name zugeordnet werden? Auch ein suchen von Objekten in globals klingt erstmal unguenstig. Denn damit missbrauchst du einfach den globalen Namensraum als deine Ablage, dafuer ist der aber nicht gedacht - im schlimmsten Fall ueberschreibt man ein existierendes Objekt, und mit Listen oder anderen Datenstrukturen bekommt man dann auch ein Problem.
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

@__deets__
Danke! Ich teile Deine Antwort auf:
a) umständlich formuliert: Mein Ziel ist(/war) mittels eines Dekorators, anwendbar auf Funktionen UND Klassen Bedienschritte in eine Liste zu schreiben.
Bei Funktionen kann ich mit <func>.__name__ den Namen in die Liste schreiben. Bei Instanzen von Klassen gibts kein Attribut __name__. So habe ich kurzerhand versucht selber herauszufinden, wie eine Instanz heisst. Dies ist mir auch gelungen.
Mein Überlegungsfehler war aber, dass ich dazu in der betreffender (Test-)Klasse eine zusätzliche Methode eingefügt habe. Bei Aufruf <Instanz>.get_name() gibt diese den Namen zurück. Ich will und kann aber nicht in jede Klasse diese Methode einpflanzen.
Hinzukommt, dass ich angenommen habe, dass beim Gebrauch (Erzeugung, Methodenaufruf, Attribut-Änderung, ..) einer Instanz einer so dekorierten Klasse mein Dekorator angestossen wird und dies mitbekommt. --> Fehlanzeige! :-( <-- Somit sehe ich im Moment keinen Weg mittels Dekorator eine Liste der Bedienschritte zu erzeugen.

b) globals()/locals(): Hier vermute ich, dass wir uns missverstanden haben.
Ich durchsuche den Namensraum nur, verändere also globals() nicht!
Wenn ich Python richtig verstanden habe, nutzt es den Namensraum, um alle Objekte zu handhaben.
Ich nutze diesen irgendwie in gleicher Weise und speichere mein Ergebnis in einem neuen Instanz-Attribut ab. Soweit sollte dies gängiger Praxis entsprechen. Als Bezeichnung des neuen Attributs wähle ich __name__, weil hier nicht vorhanden und gleich wie bei Funktionen. Einzig die Namenswahl macht mich einbisschen unsicher, weil Python diesen selber bei anderen Objekten verwendet. Könnte dies, in den Tiefen von Python, mir unbekannte Kreuzeffekte hervorrufen?

Ich liebe es Lösungen / Informationen herauszufinden, die nicht so offensichtlich auf dem Präsentierteller liegen. Dies macht die Sache interessant!
Umso dankbarer bin ich, wenn erfahrene Pythonisti meine Gedankengänge nachvollziehen und mich auf Gefahren oder Überlegungsfehler hinweisen. Wenn sie, wie oben, begründet werden, ist mein Lernerfolg noch grösser. Auch bessere, andere Lösungsweghinweise nehme ich gerne entgegen. :-)
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@JoePy: Es wird immer noch nicht wirklich klarer denn Instanzen haben keinen Namen. Da gibt es nicht wirklich etwas heraus zu finden. Also was machst Du da *tatsächlich*? Was ist in diesem Kontext *der* Name einer Instanz? Was wäre das in den folgenden Beispielen und wie würdest Du das herausfinden?

Code: Alles auswählen

    instances = [Thing(), AnotherThing(), WhatsMyName()]

Code: Alles auswählen

    one = two = three = WhatsMyName()
Man durchsucht normalerweise auch keinen globalen Namensraum dynamisch.

Was meinst Du damit das ein Dekorator ”angestossen” wird? Die Dekoratorsyntax ist nur syntaktischer Zucker für folgendes:

Code: Alles auswählen

@expr
def func(...):
    ...

# <=>

def func(...):
    ...

func = (expr)(func)
Wobei `expr` ein beliebiger Ausdruck ist, der als Ergebnis etwas liefert was eine Funktion als Argument entgegen nimmt, und eine Funktion (oder generell etwas aufrufbares) als Ergebnis liefert.

Beim dekorieren von Klassen sieht das analog aus.

`__name__` würde ich in der Tat nicht für etwas verwenden was aus Python-Sicht gar keinen Namen hat der bei der Definition bestimmt wird.

Mal angenommen Du würdest das mit Dekoratoren machen/hinbekommen, dann hätte das aber den grossen Nachteil wenn nicht gar Fehler, dass die Historie dann globaler Zustand wäre. Was keine gute Idee ist. Vorsichtig ausgedrückt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

@__blackjack__
Aus meiner Sicht eben doch! Sprich, Instanzen haben einen Namen. :-)
Ich versuch's zu erläutern:

Du erzeugst eine Klasse:
class Person:
def __init__(self,name,beitraege,wohnort):
self.name = name
self.beitraege = beitraege
self.wohnort = wohnort

def __str__(self):
return self.name self.wohnort

#jetzt eine Instanz
blkj = Person('__blackjack__', 8592, 'localhost')

"blkj" ist für mich der Name der Instanz von __blackjack__, damit arbeite ich im Prg.
Um die Anzahl Beiträge zu erhöhen:
blkj.beitraege += 1

Ich hab vielleicht was verpasst und es gibt dazu eine andere Bezeichnung!?


"Mal angenommen ..."
Bin mir nicht sicher, ob ich diesen Hinweis richtig verstehe. Ich versuche ja eine Liste (zB befehlsfolge) zu erzeugen, die mir, in der Reihenfolge des Bedienens, [<"Name" der Fkt/Instanz>,(alle notwendigen Parameter<P1>,..,<Pn>)] abspeichert.

len(befehlsfolge) -> 3 würde für mich bedeuten, dass der Bediener 3 Befehle ausgeführt hat.
befehlsfolge[1] -> ['blkj',('beitraege',9000)] hiesse: der Bediener hat im 2. Schritt das Attribut beitraege von blkj auf 9000 gesetzt.

Warum darf befehlsfolge keine globale Variable sein?
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@JoePy: Globaler Zustand ist schlecht. Das ist schlecht zu verstehen, schlecht zu testen, bei Fehlern schlecht zu debuggen, es skaliert nicht, …

Warum missbrauchst Du den Modulnamensraum als Wörterbuch? Das macht alles nur komplizierter und undurchsichtiger.

Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. `blkj` sollte nicht auf Modulebene existieren. Wo auch immer das erhöhen stattfinden soll — wenn dort das `Person`-Objekt als Argument übergeben wird, dann ist das egal wie das irgendwo anders heisst und es muss auch nicht global erreichbar sein. Wobei Du das ja noch nicht mal so verwendest, denn anscheinend versuchst Du den Namen ja irgendwie dynamisch heraus zu finden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

@__blackjack__: Ich danke für Deine Reaktion.
Python ist für mich das erste Programm, bei dem ich objektorientiert programmieren kann. Ich will und muss mich in diese neue Art des Programmierens eindenken und daran gewöhnen. Theoretisch habe ich die Grundsätze (Objekte, Vererbbarkeit, Datenkapselung, ...) "alle" gehört/gelesen/das Prinzip begriffen, aber das Herangehen an eine Aufgabe (damit Objektorientier nutzbringend ist), die konkrete Umsetzung, da stosse ich noch oft an.
Das wirst Du aus meinen Fragen und Lösungsversuchen bemerkt haben.

So fasse ich auch Deine Hinweise auf. Ich benötige noch etwas Zeit, bis ich sie auch richtig verstehe!
Meine kritischen Fragen auf Deine Antworten sollen mir helfen, diese zu begreiffen und auch sicher zu sein, dass Du siehst, was ich nicht verstehe.

Im Moment möchte ich das Prinzip eines Dekorators so richtig begreiffen.
Für mich ist ein Dekorator eine Funktion (oder eine Klasse inkl __call__) die so eingesetzt wird, dass sie aufgerufen wird, wenn im Programm die dekorierte Funktion aufgerufen wird.
Der Dekorator erhält dabei die Funktion (resp Pointer darauf) und die dieser übergebenen Argumente als seine Argumente. Mehr weiss der Dekorator über die Funktion nicht (Datenkapselung, ..). Er kann nicht in die Funktion hineinschauen, er muss mit diesen Infos und dem aktuellen Namensraum arbeiten.
Vor dem Aufruf der Funktion kann er mit diesen Informationen arbeiten, diese aufbereiten, was auch immer. Ebenso (zusätzlich nun noch mit den Rückgabewerten der Fkt) nach Aufruf der Funktion.

In meinem hier gewählten Problem, soll der Dekorator "nur" eine Liste (befehlsfolge) erzeugen, die den Bedienablauf* dokumentieren und irgendwie als "Skript" automatisch (ohne Bedienerinteraktion) genau denselben Ablauf durchspielen.
*: Ich stelle mir einen Bediener vor, welcher vor dem Bildschirm sitzt, das Programm startet und nun aus Listen Elemente mit der Maus auswählt, diese in andere Listen/Tabellen schiebt, das Programm unterstützt ihn dabei (nicht mögliche Kombinationen verhindert, sinnvolle Auswahlen vorschlägt, ...).

Um überhaupt herauszufinden, ob ich einen Dekorator programmieren kann, welcher die notwendigen Informationen über die getätigten Bedienschritte herausfinden kann, war mir der verwendete Namensraum (noch, OOP-Anfänger!) nicht wichtig.
Eingehend auf Deine Hinweise kann ich mir gut vorstellen, dass ich diese Liste und deren Verwaltung und Nutzung alles im Dekorator umsetze. Somit ist mein Dekorator eine Klasse, die spezielle Methoden zur Verwaltung und Nutzung der befehlsfolge zur Verfügung stellt.
Bin ich so auf gutem Wege?
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@JoePy: Ich denke bei dem was Du als Dekorator beschreibst ist mehr drin als ein Dekorator eigentlich ist. Der Dekorator bekommt nur die Funktion als Argument, und auch nur ein einziges mal, nämlich direkt nachdem die Funktion definiert wurde. Der wird *nicht* jedes mal aufgerufen wenn die dekorierte Funktion aufgerufen wird.

Was man machen kann ist den Dekorator eine neu erstellte Funktion zurückgeben zu lassen, die ihrerseits die übergebene Funktion aufruft. *Diese* Funktion wird jedes mal aufgerufen, denn die ersetzt die dekorierte Funktion. Siehe in meinem letzten Beitrag wofür die @-Syntax eigentlich nur syntaktischer Zucker ist. Das wurde hauptsächlich eingeführt, damit sich diese Ersetzung nicht hinter einer langen Funktion versteckt und man am Anfang der Funktionsdefinition schon sehen kann, dass die Funktion noch mal ”bearbeitet” und eventuell ersetzt wird. Gleiche Begründung bei Klassendekoratoren, wo das ja noch weiter von dem ``class`` entfernt passieren würde.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
JoePy
User
Beiträge: 7
Registriert: Dienstag 11. Mai 2021, 20:57

@__blackjack__: "Musste" Deine Antwort zuerst verdauen und gewisse Programmschnipsel ausprobieren. Ich war erstaunt und gleichzeitig in meiner Ansicht bestätigt.
Ich finde es extrem schade, dass wir nicht in einem PythonKlubLokal einander gegenübersitzen und so über Python diskutieren können. Da könnten Missverständnisse, unklare Formulierungen rasch geklärt und dann das eigentliche Brennende besprochen werden.

Ich vermute, dass ich mich ungenau ausgedrückt habe, weil ich nicht weiss, wie ich die Konstruktion nennen soll:
@deco_func
def fu_x(..):
...

könnte auch so umgesetzt werden: (so habe ich es in Büchern gefunden)
fu_x = deco_func(fu_x)

oder so, von Dir erwähnt (hab's nicht vergessen! ;-), hielt es aber zuerst als einen Verschreiber, weil es der Variante II so ähnlich sieht. Die Klammern um deco_func haben mich verwirrt:
fu_x = (deco_func)(fu_x)

Habe die Varianten ausprobiert => noch eine Schreibweise mehr!
Wenn ich dies korrekt interpretiere, haben die beiden Varianten II + III den Vorteil, dass ich eine schon definierte Funktion dekorieren kann, also auch "fremde" Funktionen! Ebenso hindert mich hier nichts daran, dem Konstrukt einen neuen Namen zu geben zB sum_plus_kommentar = deco_sum_kommentar(sum).

Nachtrag zu Deiner Antwort vom 18. Mai, 00:58:
Ja, meine DekoratorFkt (darf ich die so nennen?) soll möglichst universell sein und dynamisch herausfinden, in welchem Zusammenhang sie aufgerufen wurde. Dies ist auch der Grund, warum ich bei einer Instanz wissen möchte, welche gerade aktiv ist (der "blkj" oder der "JoePy"). Das Konstrukt reagiert ja nicht sofort auf die aktuelle Situation, dann könnte ich mir vorstellen ohne meine Suche auszukommen, aber die Situation soll ja in eine Liste geschrieben werden- -- wobei ich immer mehr zur Ansicht gelange, dass ich mir bei Klassen mit meinem Vorhaben "die Zähne ausbeisse". :-( --
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@JoePy:

re:

Code: Alles auswählen

fu_x = deco_func(fu_x)
vs.
fu_x = (deco_func)(fu_x)
Man könnte auch schreiben:

Code: Alles auswählen

fu_x = ((((((((((deco_func))))))))))(fu_x)
Die Klammern um einen Ausdruck dienen zur Disambiguierung und sind, wenn keine Ambiguität vorliegt, unnötig. Dem gegenüber stehen Klammern in der Definition und beim Aufruf von Funktionen, die syntaktisch immer notwendig sind.
In specifications, Murphy's Law supersedes Ohm's.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es wird nichts, so etwas hypergenerisch zu basteln. Wenn man eine solche Undo/Redo/Modell-Sachen macht, ist das Kollaborativ. Da müssen die Komponenten schon aufeinander angepasst sein.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Ganz universell kann man solch einen Dekorator, wie Du ihn Dir wünschst, nicht schreiben, man braucht eine gewisse Struktur, die abgebildet werden soll, die Du wahrscheinlich auch im Hinterkopf hast, aber man muß sie konkret definieren, um solch einen Dekorator zu implementieren.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

JoePy hat geschrieben: Samstag 15. Mai 2021, 16:57 Ich habe weiter gepröbelt und eine simple Lösung (get_name) gefunden, um den "Namen" einer Instanz herauszufinden (die Instanz sucht ihren Self-Wert in globals).
Jede Instanz einer Python-Klasse kennt ihre Klasse in "<instanz>.__class__", jede Klasse kennt ihren Namen in "<klassenname>.__name__". Daraus folgt: Du kannst Dir das mit globals() sparen und einfach "<instanz>.__class__.__name__" abfragen... "Ich hab da mal was vorbereitet" (Jean Pütz, RIP):

Code: Alles auswählen

#!/usr/bin/env python
import functools

def log(f):
    '''decorator that logs method calls with params'''
    @functools.wraps(f)
    def inner(*args, **kwargs):
        arguments = ', '.join(args[1:])
        keywords = ', '.join(['{}={!r}'.format(i, kwargs[i]) for i in kwargs])
        allargs = ', '.join(i for i in [arguments, keywords] if i)
        print('in log(): called {}.{}({})'.format(
            args[0].__class__.__name__, f.__name__, allargs))
        return f
    return inner


class A:
    @classmethod
    def show(obj):
        print('in show(), object: {}'.format(str(obj)))
        print('in show(), object.__name__: {}'.format(str(obj.__name__)))
        print('in show(), object.__class__: {}'.format(str(obj.__class__)))
        print()

    @log
    def dings(self, *args, **kwargs):
        print('in dings()')


if __name__ == '__main__':
    A.show()
    a = A()
    a.show()
    a.dings()
    a.dings('klaus', 'dieter')
    a.dings('klaus', 'dieter', name='hanswurst')
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

JoePy hat geschrieben: Montag 17. Mai 2021, 22:50
blkj = Person('__blackjack__', 8592, 'localhost')

"blkj" ist für mich der Name der Instanz von __blackjack__, damit arbeite ich im Prg.
Um die Anzahl Beiträge zu erhöhen:
blkj.beitraege += 1

Ich hab vielleicht was verpasst und es gibt dazu eine andere Bezeichnung!?
Najaaa... das ist jetzt nicht ganz einfach zu erklären, aber ich versuch's trotzdem mal. "blkj" ist nur ein Variablenname, also ein frei gewählter Bezeichner ohne jeden Informationsgehalt und für Dein Vorhaben vollkommen bedeutungslos. Diesen Bezeichner gibt es in Deinem Programm nur, um etwas anzusprechen, das die ganzen wichtigen Informationen enthält, nämlich der Wert von "blkj", in diesem Fall also eine Instanz Deiner Klasse "Person".
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@LukeNukem: join funktioniert nur mit String-Elementen, ein `a.dings(17)` liefert einen Fehler.
Bei keywords ist die Listcomprehension unnötig, da ein Generatorausdruck reicht. `i` ist ein schlechter Name für einen Schlüssel, da man mit i einen Index verbindet. Statt i und kwargs[ i] würde man mit kwargs.items() gleich über Schlüssel und Wert iterieren. Hier benutzt Du auch die Repräsentation des Wertes, und nicht wie bei arguments nur den Inhalt der Strings.
In `inner` solltest Du `f` aufrufen und nicht nur `f` zurückgeben.

In A.show würde man das Argument statt `obj` `cls` nennen, dann würde man gleich sehen, dass es sich um die Klasse handelt und nicht um irgendein Objekt. Die str-Aufrufe sind allesam überflüssig, weil das format schon erledigt.

Dem Fragensteller geht es ja gerade darum, Aktionen auf dem Objekt bjkl zu loggen, und nicht abstrakt auf irgendwelche Funktionsaufrufe ohne Bezug zur Instanz.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

pillmuncher hat geschrieben: Donnerstag 20. Mai 2021, 16:57

Code: Alles auswählen

fu_x = (deco_func)(fu_x)
Vermutlich ist Dein Python-Fu stärker als meines, aber wäre

Code: Alles auswählen

fu_x = deco_func((fu_x))
nicht irgendwie... richtiger? ;-)
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@LukeNukem: Man könnte auch das hier schreiben:

Code: Alles auswählen

((((((((((fu_x)))))))))) = ((((((((((((((((((((deco_func))))))))))((((((((((fu_x))))))))))))))))))))
Kein Unterschied im Ergebnis.
In specifications, Murphy's Law supersedes Ohm's.
Antworten