Seite 1 von 1

Klasse eines Objectes ermitteln

Verfasst: Montag 23. April 2007, 11:35
von ferdi09
Hallo,
ich habe folgende Frage: Wenn ich einer Funktion als übergabeparameter ein Objekt einer Klasse übergebe, kann die Funktion ermitteln zu weelcher Klasse dieses Objekt gehört? Ich möchte auf unterschiedliche Objekte unterschiedlich reagieren.
Mal angenommen A wäre eine Klasse. Dann bekomme ich mit type ja nur mit das ich eine Instanz habe.

Code: Alles auswählen

>>> a = A()
>>> type(a)
<type 'instance'>
Wie bekomme ich die konkrete Klasse?

Gruss
Holger

Verfasst: Montag 23. April 2007, 11:40
von BlackJack

Code: Alles auswählen

In [50]: class A(object):
   ....:     pass
   ....:

In [51]: a = A()

In [52]: a.__class__
Out[52]: <class '__main__.A'>

Verfasst: Montag 23. April 2007, 11:49
von ferdi09
Vielen Dank das hat mir sehr geholfen. Jetzt kann ich mit:

Code: Alles auswählen

if a.__class__ == A:
  print "Objekt von A"
else:
  print "Kein Objekt von A
alles weitere lösen.

Verfasst: Montag 23. April 2007, 12:04
von EyDu
Schau dir einfach mal die Built-In-Funktion "isinstance" an.

Bei der Gelegenheit möchte ich aber noch mal anmerken, dass ein Typen-Test in den seltensten Fällen (die sind wirklich sehr selten!) sinnvoll ist. Unter Umständen solltest du dir noch mal Gedanken über dein Design machen.

Verfasst: Montag 23. April 2007, 12:12
von ferdi09
Hm, mal angenommen ich habe eine einfache Klasse Bruch mit Zaehler und Nenner. Und ich möchte die __add__ Funktion überladen und somit Bruch + Bruch erlauben und Bruch + int.

Code: Alles auswählen

def __add__(self, b):
  if isinstance(a, Bruch):
     print "add zaehler und nenner"
  elif isinstance(b, int):
     print "add int to bruch"
Dann muss ich doch in der __add__ Methode den Typ erfragen und entweder einen int oder einen Bruch addieren.
Mir fällt da keine andere Möglichkeit ein.

Verfasst: Montag 23. April 2007, 12:24
von mitsuhiko
Und hier kommen wir zum Metaklassenproblem. Bei Klassen "neuen Stils" ist type(instance_of_class) == Class. Bei Klassen altern Stils ist es <type 'instance'>. Eine Klasse neuen Stils bekommt man, indem man von "object" ableitet oder von einer Klasse, die bereits neuen Stils ist.

Das zeigt mal das Problem:

Code: Alles auswählen

>>> class Foo(object):
...  pass
... 
>>> type(Foo())
<class '__main__.Foo'>
>>> class Bar:
...  pass
... 
>>> type(Bar())
<type 'instance'>
"old-style classes" verschwinden mit Python3.

Verfasst: Montag 23. April 2007, 12:25
von EyDu
Nein, so was löst man anders:

Code: Alles auswählen

class Integer(object):
    def __add__(self, a):
        a._add_with_int(self)
    
    def _add_with_int(self, i):
        print "int + int"
    
    def _adf_with_fraction(self, f):
        print "fraction + int"

class Fraction(object):
    def __add__(self, a):
        a._add_with_fraction(self):
    
    def _add_with_int(self, i):
        print "int + fraction"
    
    def _add_with_fraction(self, f):
        print "fraction + fraction"
Kommt dir vielleicht ein wenig viel Code für so wenig Aufgabe vor, aber wenn das Projekt groesser wird lohnt es sich. Anders verlierst du nur die Übersicht und Erweitern um neue Typen wird sehr schwierig.

Verfasst: Montag 23. April 2007, 12:41
von ferdi09
@EyDu

Das ist natürlich elegant. Nur was mir daran nicht gefällt, das ein aufrufen von z.B. fraction + 3 nicht funktioniert, da die 3 als int initialisiert wird und nicht als Typ deiner Klasse Integer. Oder habe ich wieder was übersehen?

Verfasst: Montag 23. April 2007, 12:55
von BlackJack
@blackbird: Das Problem gibt's hier aber nicht, oder habe ich etwas übersehen?

Verfasst: Montag 23. April 2007, 13:14
von EyDu
ferdi09 hat geschrieben:@EyDu

Das ist natürlich elegant. Nur was mir daran nicht gefällt, das ein aufrufen von z.B. fraction + 3 nicht funktioniert, da die 3 als int initialisiert wird und nicht als Typ deiner Klasse Integer. Oder habe ich wieder was übersehen?
Das siehst du so schon richtig, aber es kommt halt immer darauf an, was man machen möchte. Wenn du vor hast eine riesige Mathebibliothek zu schreiben würde ich meine Lösung vorziehen und Aufrufe der Form "Integer(42)" vorziehen, da ich so auf der sicheren Seite bin. Wenn du aber nur ab und zu mal Brüche benötigst, die mit Integern addiert werden sollen wird deine Lösung sicherlich ausreichen.

Probleme hast du nur, wenn du dein System irgendwann mal erweitern möchtest (und das soll öfter vorkommen als man glaubt) und du dann an allen Ecken Inkonsistenzen bekommst um die du IRGENDWIE herumarbeiten mußt.

Außerdem finde ich es schöner Probleme mit Design zu erschlagen, als mit irgend welchen wirren Methoden. Vielleicht liegt es aber auch daran, dass die Projekte an denen ich arbeite so einen Umfang haben, dass es anders kaum noch geht.

Verfasst: Montag 23. April 2007, 13:18
von mitsuhiko
BlackJack hat geschrieben:@blackbird: Das Problem gibt's hier aber nicht, oder habe ich etwas übersehen?
Dann hast du einen aktuellen Python3 build oder __metaclass__ = type im globalen Namespace stehen.

Verfasst: Montag 23. April 2007, 15:14
von BlackJack
Du solltest mir nicht sagen was Du glaubst, was ich in meinem Quelltext zu stehen habe, sondern wo ich jetzt konkret das "Problem" mit `type()` in diesem Thread übersehen habe!? Bei den beiden Lösungsvorschlägen, die `type()` gar nicht benutzen. Weder `obj.__class__` noch `isinstance()` sind davon betroffen.

Deine Information ist schon richtig, ist aber "überflüssig". Denn das `type()` keine Lösung ist hat der OP gleich im ersten Beitrag selbst gesagt und nach echten Lösungen gefragt.

Verfasst: Montag 23. April 2007, 17:08
von birkenfeld
EyDu hat geschrieben:
ferdi09 hat geschrieben:@EyDu

Das ist natürlich elegant. Nur was mir daran nicht gefällt, das ein aufrufen von z.B. fraction + 3 nicht funktioniert, da die 3 als int initialisiert wird und nicht als Typ deiner Klasse Integer. Oder habe ich wieder was übersehen?
Das siehst du so schon richtig, aber es kommt halt immer darauf an, was man machen möchte. Wenn du vor hast eine riesige Mathebibliothek zu schreiben würde ich meine Lösung vorziehen und Aufrufe der Form "Integer(42)" vorziehen, da ich so auf der sicheren Seite bin. Wenn du aber nur ab und zu mal Brüche benötigst, die mit Integern addiert werden sollen wird deine Lösung sicherlich ausreichen.
Bah, Integer als solche wrappen zu müssen ist irgendwie krank.
Außerdem finde ich es schöner Probleme mit Design zu erschlagen, als mit irgend welchen wirren Methoden. Vielleicht liegt es aber auch daran, dass die Projekte an denen ich arbeite so einen Umfang haben, dass es anders kaum noch geht.
Darum löst man sowas auch mit Multimethods, am besten einer Implementierung mit automatischer Typkonvertierung.

Verfasst: Dienstag 24. April 2007, 08:13
von mitsuhiko
BlackJack hat geschrieben:Du solltest mir nicht sagen was Du glaubst, was ich in meinem Quelltext zu stehen habe, sondern wo ich jetzt konkret das "Problem" mit `type()` in diesem Thread übersehen habe!? Bei den beiden Lösungsvorschlägen, die `type()` gar nicht benutzen. Weder `obj.__class__` noch `isinstance()` sind davon betroffen.
Ich bezog mich auf die Verwirrung des Fragestellers, warum type() bei ihm wieder erwarten Instance zurück gibt und nicht seine Klasse.