[Erledigt] Feststellen des (Eltern)Klassennames

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.
BlackJack

Das ist recht umständlich. Informationen aus einer Zeichenkettendarstellung zu parsen anstatt auf diese direkt zuzugreifen ist zum Beispiel fehleranfällig. `__mro__` enthält Klassen- oder Typ-Objekte, die haben ein `__name__`-Attribut, und das freundlicherweise sogar ohne den "Pfad" durch die Module und Packages.

`ret` und einiges an Prüfcode könnte man einsparen wenn man am Ende ein ``return True`` schreibt und die Funktion verlässt mit ``return False`` verlässt, wenn eine der Bedingungen nicht erfüllt ist.

Und ich verstehe immer noch nicht warum der Klassenname wichtiger ist als die Funktionalität, die die Klasse bietet.

Das ganze etwas kompakter:

Code: Alles auswählen

from inspect import getmro
from itertools import imap

def check(motor, needs):
    def check_need((class_, motor_name)):
        class_names = (c.__name__ for c in getmro(motor.__class__))
        return class_.__name__ in class_names and motor.name == motor_name
    
    return any(imap(check_need, needs))
ChrisGTJ
User
Beiträge: 105
Registriert: Mittwoch 22. August 2007, 15:45

Hi BlackJack,

das wird ja wieder mal eine Lehrstunde in Python, prima, danke dafür! Python ist derartig vielfältig, daß ich aus der (beschränkten?) Sicht meiner C Erfahrungen gar nicht auf die Idee komme, daß da noch was ist. Allein die Geschichte mit

Code: Alles auswählen

result = b in liste
hat mich mal wieder völlig Überrascht...

Ich habe Deinen Code mal übernommen und ausgeführt, bekomme aber eine Exception:

Code: Alles auswählen

Traceback (most recent call last):
  File "D:\Program Files\eclipse 3.3\plugins\org.python.pydev.debug_1.3.8\pysrc\pydevd.py", line 575, in trace_dispatch
    return dbFrame.trace_dispatch(frame, event, arg)
  File "D:\Program Files\eclipse 3.3\plugins\org.python.pydev.debug_1.3.8\pysrc\pydevd_frame.py", line 53, in trace_dispatch
    for b, condition, func_name in breakpoint.values():
GeneratorExit
Exception exceptions.SystemError: 'error return without exception set' in <generator object at 0x00BF64E0> ignored
Könnte das das Ende des Generators sein? Wenn ich das Skript ohne den Debugger laufen lasse, sehe ich die Exception nicht.

Frage nebenbei: Wieso baut die folgende Zeile überhaupt einen Generator?

[code = py]
class_name = (c.__name__ for c in getmro(motor.__class__))
[/code]


Was die Frage nach der Frage angeht:
Ich verstehe nicht, was Du nicht verstehst ;). Soll heißen, daß für mich die Aufgabenstellung aus unserem Kontext heraus völlig klar und logisch ist, und daher auch unser Lösungsansatz. Aber dieser Lösungsansatz beruht möglicherweise auf einem zu geringen Verständnis der Konzepte von Python und deren Möglichkeiten.

Wir machen es uns hier auch vielleicht etwas einfach, kann sein. Du hast in einem früheren Beitrag hierzu gefragt, warum wir nicht auf Attribute abtesten. Möglicherweise denke ich zu kurz, aber nehmen wir an, eines der Attribute ist die Anbindung an die Steuerung, einmal über einen CAN Bus und zum anderen mal über eine RS232 Leitung. Dann gibt es mindestens zwei Testkategorien:

1.) Tests, die die Fähigkeiten des Motors testen, bei denen das Kommunikationsmedium völlig egal ist.
2.) Tests, die die Kommunikation selber testen oder aber spezielle Möglichkeiten des Kanals ausnutzen.

Wenn wir jetzt eine Testsuite aufbauen, dann möchten wir klassifizieren und abgleichen können,
- was ein spezieller Test benötigt und
- was ein Testaufbau hergibt.

Der Testentwickler kann nun einfach (na ja, da das mit dem String nicht geht, ist es nicht mehr ganz so einfach...) aufschreiben, was sein Test erfordert. Wir wollen bewußt eine feste Syntax vorgeben, damit sich der Anwender klar wird, was er tut (im Gegensatz zu dem, was er tun will ;))

Reicht das an Erklärung? Wenn es Ideen gibt, das auf anderem Wege zu erreichen, dann nur her damit...

Gruß,

Christoph
ChrisGTJ
User
Beiträge: 105
Registriert: Mittwoch 22. August 2007, 15:45

OT:
BlackJack hat geschrieben: `ret` und einiges an Prüfcode könnte man einsparen wenn man am Ende ein ``return True`` schreibt und die Funktion verlässt mit ``return False`` verlässt, wenn eine der Bedingungen nicht erfüllt ist.
Nun, dazu kann ich nur sagen, daß die meisten Programmierrichtlinien, die mir bisher untergekommen sind, verlangen, daß es für eine Funktion / Methode nur *einen* Aussprungpunkt gibt. Man kann dazu stehen, wie man will, aber oft macht das den Code sicherer und besser lesbar.

:twisted: Wobei das mit der Lesbarkeit und Python in meinen Augen eh ein besonderes Thema ist, keine Klammerung, viele Operationen in einer Zeile und zwischen Klammer und dem nächsten Wort kein Leerzeichen und so... :twisted:

Aber das ist natürlich Geschmackssache und hängt auch direkt von den Fähigkeiten, den Code zu lesen und gleich zu verstehen, ab...

Gruß,

Christoph
ChrisGTJ
User
Beiträge: 105
Registriert: Mittwoch 22. August 2007, 15:45

Oh ja, dann noch eine Frage:

Warum

Code: Alles auswählen

getmro(motor.__class__)
anstelle von

Code: Alles auswählen

motor.__class__.__mro__
?
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Der Übersichtlichkeit halber... oder schreibst du sowas oO:

Code: Alles auswählen

foo = "bar"
foo.__len__()
anstelle von

Code: Alles auswählen

foo = "bar"
len(foo)
BlackJack

Die Ausnahme scheint ja aus dem pydev Debugger zu kommen. Keine Ahnung was das ist, aber ich würde mal auf einen Fehler im Debugger tippen.

Die Zeile ist ein Generator-Ausdruck. Die sind relativ neu und im Grunde das gleiche wie "list comrehension" nur eben "lazy". Ganz allgemein gehalten:

Code: Alles auswählen

# Erzeugt eine Liste.
result = [func(x) for x in if pred(x)]

# Erzeugt einen Generator/Iterator
result = (func(x) for x in if pred(x))
Der Generatorausdruck hat halt den Vorteil, dass die Elemente erst ausgewertet werden wenn sie angefordert werden und nicht wie bei der "list comprehension" sofort. Das bedeutet im Quelltext oben, dass der ``in``-Test abbricht sobald das erste mal `True` herausgekommen ist.

Diese "ein Austrittspunkt"-Regel macht zwar Sinn, aber es gibt auch Ausnahmen. Bei kurzen Funktionen wo man am Ende ein ``return KONSTANTE`` sieht, ist irgendwie klar, dass innerhalb der Funktion noch andere ``return``\s stecken werden. Und in diesem Fall denke ich ist die Ausnahme, dass Quelltext nicht sicherer wird, wenn einen diese Regel dazu zwingt umständlich einen Rückgabewert über Schleifendurchläufe zu "retten". Mich hatte das ``return = False`` mittendrin zum Beispiel erst einmal verwirrt.

`inspect.getmro()` habe ich verwendet um ein wenig direkten Zugriff auf __magische__ Attribute zu vermeiden. Ich weiss jetzt aus dem Kopf nicht wie sicher __mro__ vorhanden sein muss, oder ob es ein Implementierungsdetail ist, aber ich würde erwarten, dass `inspect.getmro()` auch in Pythonimplementierungen funktioniert, die dieses Attribut nicht an Objekten anbieten.
ChrisGTJ
User
Beiträge: 105
Registriert: Mittwoch 22. August 2007, 15:45

@BlackVivi: Natürlich nicht... ;)

@BlackJack:

Danke, das sind einleuchtene Erklärungen.

Deinen Ausführungen zum Austrittspunkt stimme ich voll und ganz zu. Regeln sind wichtig und notwendig, aber starr dürfen sie nicht sein. Nach dem Motto: Gebogen dürfen die Wege sein, nur krumm nicht. In C/C++ nutze ich manchmal sogar goto...

Gruß,

Christoph
Antworten