Auf user-defined class prüfen...

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

wie lässt sich überprüfen, ob ein Exemplar von einer user-defined class stammt?

Code: Alles auswählen

class Test(object):
    def __init__(self):
        pass

Code: Alles auswählen

>>> t = Test()
>>> not isinstance(t, (int, float, long, str, tuple, .....))
Das kann's ja wohl nicht sein... :)

Die Doku spricht an verschiedenen Stellen von built-in types und user-defined classes. Wie ich allerdings prüfen kann, ob ein Objekt eben kein built-in type ist, das konnte ich nicht finden.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Wenn, dann ``issubclass``. Und der Ansatz erwischt auch alle ``class myInt(int): pass``.

Eine Moeglichkeit waere es, Attribute zu setzen und einen `TypeError` abzufangen. Man muss dabei aber aufpassen, dass man keine Namen ueberschreibt also schon mit dem jeweiligen `__dict__` abgleichen.

Aber um Himmels willen: Warum brauchst du das?
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Code: Alles auswählen

>>> (42).__class__ in vars(__builtins__).values()
True
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die im Modul types definierten Namen dürften so ziemlich alles an Builtin-Typen abdecken. Leider existiert dort kein `__all__` oder eine ähnliche "zentrale Stelle", die man nutzen könnte, so dass man die Typen selbst einsammeln muss:

Code: Alles auswählen

import types
builtin_types = [cls for (name, cls) in vars(types).items() if name.endswith('Type')]
Das dürfte immer funktionieren, da laut Doku die Namen grundsätzlich auf `Type` enden.

Da du ja offenbar vererbte Typen ausschließen willst, solltest du natürlich kein `isinstance()` nutzen, sondern den dem Konzept von Python zuwiderlaufenden exakten Typcheck nutzen:

Code: Alles auswählen

def is_builtin_type(instance):
    return type(instance) in builtin_types
//edit: Huch, es soll ja gerade *kein* Builtin-Typ sein. :o

Aber eigentlich ist die Funktion ja trotzdem noch okay:

Code: Alles auswählen

if not is_builtin_type(instance):
    [...]
Wobei ich gerade sehe, dass das Modul in Python 3.x umgebaut wurde und die direkt erreichbaren Standard-Typen rausgeschmissen hat. :(

Trotz alledem: Wozu brauchst du das? Ich bin fast sicher, dass das, was du vorhast (egal, was es ist), auch anders lösbar ist. ;)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wenn ich Euch jetzt sage, wozu ich das brauche, wird sicherlich gleich ein Sturm der Entrüstung über mir hereinbrechen... :D
Ok, ich hab' eine 'Observable()', deren Rückruffunktionen ich samt dazugehörige Parameter übergebe:

Code: Alles auswählen

class Test(object):
    def __init__(self):
        self.a = 1
        self.b = 2

def test():
    return 5, 6

def callback(a, b):
    print a, b

Code: Alles auswählen

>>> t = Test()
>>> o = Observable()
>>> o.add_observer(callback, (t, 'a'), (t, 'b'))
>>> o.add_observer(callback, (3, 4))
>>> o.add_observer(callback, (test,))
>>> o.notify_observers()
>>> 1, 2
>>> 3, 4
>>> 5, 6
Ihr könnt' Euch sicher denken, woran ich festhänge: 'add_observer' überprüft zunächst, ob das erste Element callable ist. Wenn nicht, kann es sich entweder um ein Exemplar mit Attribut (t, 'a') oder um feste Parameter (3, 4) handeln.
Nachdem nun sowohl 't' wie auch '3' nicht callable sind aber nur 't' ein Attribut 'a' hat, '3' aber kein Attribut '4', habe ich das erstmal mit 'try: ... except (TypeError, AttributeError):' gelöst.

'try: ... except: ...' mutet mir aber immer etwas wie ein Hack an, deshalb war ich auf der Suche nach einer Möglichkeit, 't' von '3' klar unterscheiden zu können.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

mutetella hat geschrieben: 'try: ... except: ...' mutet mir aber immer etwas wie ein Hack an, deshalb war ich auf der Suche nach einer Möglichkeit, 't' von '3' klar unterscheiden zu können.
Zumindest ist EAPF ein, wenn nicht der, empfohlener Programmierstil in Python, deshalb verstehe ich nicht, wieso dir das wie ein "Hack" vorkommt :?:
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dann mach doch das, was du beschrieben hast: Prüfung auf Aufrufbarkeit(`try...except` oder Bultin-Funktion `callable()`), ansonsten teste es als Attribut (`hasattr()`) und als letzte Lösung gibst du das Argument unverändert zurück.
lunar

@mutetella: Ich würde sagen, Du wiederholst Deine Fehler, und versuchst wieder, ein Objekt bzw. eine Funktion zu entwerfen, die einfach zu viel macht. Gerade die Attributerkennung ist viel zu magisch.

"add_observer()" sollte ohne Überprüfung eine aufrufbare Funktion entgegen nehmen, und nicht versucht zu erraten, was die Argumente sonst noch bedeuten könnten. Der Aufrufer kann ja immer noch eine lokale oder partielle Funktionen oder einen Lambda-Ausdruck übergeben, um beispielsweise Attribute zu setzen. Statt "o.add_observer(callback, (3, 4))" also einfach "o.add_observer(partial(callback, 3, 4))" und schon wird die ganze Magie und damit das ganze Problem hinfällig.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@lunar:
Du hast Recht, das ist immer wieder die Falle, in die ich tappe. Zuerst schreibe ich eine einfache Funktion, die 'irgendwas' bekommt, was damit macht und dann wieder ausspuckt. Dann kommt der Punkt, an dem dieses 'irgendwas' auch 'was anderes' sein könnte. Ab hier beginnt dann der Wahnsinn dieser 'if-else-achja, und wenn-dann'-Abfragerei.
Und das alles motiviert sich aus der Vorstellung, dass irgendwann einmal irgendwer diese Funktion ebenfalls gebrauchen könnte und sie deshalb möglichst breit und tolerant angelegt sein sollte.
Nicht selten muss ich mich dann selbst dabei bremsen, nicht noch Funktionalitäten zu implementieren, die ich selbst überhaupt nicht brauche...

Jetzt mal ehrlich: Sind eure Funktionen immer gänzlich trocken und zweckgebunden? Oder steht ihr hin und wieder auch in der Versuchung, da noch was cooles zu machen, das letztlich purer Luxus ist?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

So gut wie nie - ich wüßte schon gar nicht, wie ich nicht genutzte Funktionalität vernünftig warten könnte. Wenn Funktionen zusätzliche Optionen beherrschen sollen, bekommen sie halt eben das: Extraoptionen, die per default nicht gesetzt sind.

Was aber manachmal vorkommt ist so was:

Code: Alles auswählen

import numpy

def foo(iterable, ...):
    iterable = numpy.array(iterable) # hier kann natürlich eine Liste ihren Typ wechseln und zum numpy-array konvertieren
    ...
Wobei gerade diese Anwendung eigene Fallstricke bereit hält (schließlich können auch z. B. Strings in numpy-arrays umgewandelt werden, obwohl die Funktion wahrscheinlich nichts damit anfangen könnte), aber das ist eine Frage, der sich andere Paradigmen widmen. Gaaanz selten prüfe ich vorher mittels isinstance().
Antworten