Klasse eines Objectes ermitteln

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
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Montag 23. April 2007, 11:35

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
BlackJack

Montag 23. April 2007, 11:40

Code: Alles auswählen

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

In [51]: a = A()

In [52]: a.__class__
Out[52]: <class '__main__.A'>
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Montag 23. April 2007, 11:49

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.
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Montag 23. April 2007, 12:04

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.
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Montag 23. April 2007, 12:12

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.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Montag 23. April 2007, 12:24

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.
TUFKAB – the user formerly known as blackbird
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Montag 23. April 2007, 12:25

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.
ferdi09
User
Beiträge: 16
Registriert: Donnerstag 18. Januar 2007, 11:23

Montag 23. April 2007, 12:41

@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?
BlackJack

Montag 23. April 2007, 12:55

@blackbird: Das Problem gibt's hier aber nicht, oder habe ich etwas übersehen?
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Montag 23. April 2007, 13:14

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.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Montag 23. April 2007, 13:18

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.
TUFKAB – the user formerly known as blackbird
BlackJack

Montag 23. April 2007, 15:14

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.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Montag 23. April 2007, 17:08

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.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Dienstag 24. April 2007, 08:13

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.
TUFKAB – the user formerly known as blackbird
Antworten