Methoden in Objecten finden

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.
Antworten
monaid
User
Beiträge: 14
Registriert: Mittwoch 13. Juni 2018, 17:19

Hallo MitForisten,

ich versuche mir alle __doc__ in Klassen ausgeben zu lassen, und komme nicht weiter.

Code: Alles auswählen


class A(object):
  
    
   def list_helps(self):
     """ print all available commands"""
     className = type(self)
     for m in className.__dict__:
       if not m.startswith('_'):
	 print (className.__dict__[m].__doc__)

class B(A):
   def testFn(self):
     """ this is a test""" 
     pass

a = A()
a.list_helps()
b= B()
b.list_helps()


Die Ausgabe ist:

print all available commands
this is a test

b.list_helps
gibt mir also keine __doc__ von a aus. Offensichtlich habe ich die Vererbung nicht verstanden.
Wie kann man sowas machen ?
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

Moin,

es sieht so aus, als wolltest du die Builtin-Funktion `help` nachprogrammieren. Machst du das, um zu lernen, oder willst du das wirklich benutzen?

Das funktioniert so nicht, wie du das geschrieben hast, weil die Klasse `B` kein `"list_helps"` in ihrem `__dict__` hat. Klassen haben nur die eigenen Attribute in ihrem `__dict__`. Wenn jetzt auf einer Instanz einer Klasse ein Attribut abgefragt wird, das nicht in der Instanz oder der Klasse der Instanz vorhanden ist, so wird die Vererbungshierarchie der Klasse nach oben hin abgesucht. Die Stichworte wäre MRO und C3-Linearisierung. Das ganze ist näher in Data Model beschrieben.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Du müsstest Dich halt selbst durch die Vererbungshierarchie hangeln, denn das `__dict__` von `B` enthält nur die Sachen die in `B` definiert sind. Oder weniger, denn nicht alles muss in dem Attribut stecken! Ein Grund nicht selbst auf die ”magischen” Attribute zuzugreifen. Schau Dir mal die `dir()`-Funktion an. Da bekommst Du nicht nur von `B` beide Methoden, sondern sogar von `b`:

Code: Alles auswählen

In [40]: dir(B)
Out[40]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'list_helps',
 'testFn']

In [41]: dir(b)
Out[41]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'list_helps',
 'testFn']
Der Name `className` ist falsch, denn das ist nicht der Name der der Klasse, sondern tatsächlich die Klasse selbst! Da `class` als Name nicht geht ist die übliche Konvention einen Unterstrich anzuhängen: `class_`. Ausnahme sind Klassen die bei Methoden für `classmethod()` übergeben werden — das Argument heisst dort üblicherweise `cls`.

Vielleicht magst Du Dir auch mal das `inspect`-Modul anschauen:

Code: Alles auswählen

In [46]: inspect.getmembers(b, inspect.ismethod)
Out[46]: 
[('list_helps',
  <bound method B.list_helps of <__main__.B object at 0x7f0f1e628250>>),
 ('testFn', <bound method B.testFn of <__main__.B object at 0x7f0f1e628250>>)]
Und `inspect.getdoc()` ist vielleicht auch interessant.

Edit: Und das `pydoc`-Modul ist vielleicht auch interessant, denn darauf baut die eingebaute `help()`-Funktion auf.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
monaid
User
Beiträge: 14
Registriert: Mittwoch 13. Juni 2018, 17:19

Danke für Eure Hilfestellungen,
ja ich wollte das wirklich benutzen (und ich mache das, um zu lernen).

ich will SocketServer.StreamRequestHandler ein Object mitgeben, bei dem alle möglichen Actionen methoden sind, welche mit process_ beginnen. Um mir dynamisch alle Möglichkeiten (mit Erklärung) ausgeben zu lassen dachte ich dies wäre ein probates Mittel.

Angefangen habe ich wirklich auch mit

Code: Alles auswählen

for m in dir (A):
   print A.__dict__[m].__doc_
da kam ich aber vom der hardcoded Klasse nicht los, was die Methode ja nicht sinnvoll vererbar macht.
Danke auch für die Tips mit den Namenskonventionen, ich werde das in Zukunft versuchen zu beachten.
Leider komme ich erst Freitag dazu mir das anzuschauen, Euch noch schönen Abend :)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Bei dem Code musst Du vorsichtig sein, denn nicht alles was `dir()` liefert muss auch im `__dict__()` sein. Wie gesagt, diese magischen Namen möglichst vermeiden wenn es einen anderen offiziellen weg gibt. Wenn Du das nicht fest an der Klasse haben willst, kannst in einer Methode ja `self` statt `A` verwenden. Und statt direktem Zugriff auf das `__dict__` das jeweilige Attribut mit der offiziellen Funktion zum Abfragen von Attributen abfragen:

Code: Alles auswählen

        # ...
        for name in dir(self):
            try:
                print(getattr(self, name).__doc__)
            except AttributeError:
                pass
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
monaid
User
Beiträge: 14
Registriert: Mittwoch 13. Juni 2018, 17:19

Hey Danke _blackjack_,
das hat mir schon sehr geholfen.
getatttr() hatte ich vorher nicht auf dem Schirm, wohl auch deswegen sind meine Versuche vorher gescheitert.
Morgen baue ich das mal ein :)
Antworten