Typ von Instanzen in Python2.2 (abgeleitet von object)

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
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hallo,

ich möchte gern dynamisch zur Laufzeit den Typ einer Variable ermitteln, insbesondere ob ich es mit der Instanz einer Klasse oder einem Dictionary zu tun habe. Bei "alten" Klassen, die nicht von object erben, kann ich das relative einfach mit dem Vergleich

Code: Alles auswählen

type(obj) == types.InstanceType ermitteln
Wenn aber von object geerbt wird, gibt es in types anscheinend keinen Typ, der der Rückgabe von type(obj) entspricht:

Code: Alles auswählen

import types

class TestDict(object):
#class TestDict:
    def __init__(self, dict):
        self._dict = dict
    def __getitem__(self, item):
        return self._dict[item]

if "__main__" == __name__:

    td = TestDict({1 : 2})
    print "type(td)=%s" % type(td)
    for typeName in dir(types):
        stmt = "type(td) == types.%s" % typeName
        print "stmt=%s %s" % (stmt, eval(stmt))
Hat jemand eine Idee, wie ich feststellen kann, ob einer Variable eine Instanz einer user-definierten Klasse zugewiesen wurde?

Vielen Dank im Voraus

Wolfgang
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Code: Alles auswählen

isinstance(object, classinfo)
Return true if the object argument is an instance of the classinfo argument, or of a (direct or indirect) subclass thereof. Also return true if classinfo is a type object and object is an object of that type. If object is not a class instance or an object of the given type, the function always returns false. If classinfo is neither a class object nor a type object, it may be a tuple of class or type objects, or may recursively contain other such tuples (other sequence types are not accepted). If classinfo is not a class, type, or tuple of classes, types, and such tuples, a TypeError exception is raised. Changed in version 2.2: Support for a tuple of type information was added.

http://www.python.org/doc/2.3/lib/built-in-funcs.html
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hallo Dill,

vielen Dank für Deine Antwort, aber das hilft mir nicht wirklich weiter:

Code: Alles auswählen

aDict = {1:2}
aList =  [4, 5, 6]
aString = "a string"

for obj in [aDict, aList, aString]:
    print "obj:%s is instance of object:%s" % (obj, isinstance(obj, object))
Wenn man das ausführt, sieht man, dass alle o.a. Objekte als Instanzen von object aufgefasst werden, das gilt also auch für builtin-Typen. Ich will aber eigentlich gerade wissen, ob eine Variable eine Instanz irgendeines User-defined-Typs ist. instanceof könnte ich nur überprüfen, ob obj Instanz eines bestimmten Typen ist.

Gruss

Wolfgang
BlackJack

Die Menge der eingebauten Datentypen ist ja recht überschaubar, teste doch einfach explizit auf alle.

Interessante Frage ist natürlich warum Du das überhaupt wissen willst.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

sry, habe dich falsch verstanden.

du könntest folgendes machen:

Code: Alles auswählen

class user_object( object ):
    pass
alle deine eigenen klassen lässt du von user_object, statt von object erben, dann kannst du mit isinstance(zu_testen, user_object) testen ob
das eine (VON DIR) selbsterstelle klase ist.

ist aber ziemlich schwachsinnig ... :)

denke nicht, dass es einen anderen weg gibt, da eben alles von object erbt.

oder wie BJ sagte:

Code: Alles auswählen

>>> builtins.append(type(""))
>>> builtins.append(type(0))
>>> builtins.append(type(0l))
>>> ...
>>> builtins
[<type 'str'>, <type 'int'>, <type 'long'>, ...]
>>> if type(x) in builtins:
             print "BUILTIN"


aber was willst du damit erreichen ...?
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

ws hat geschrieben:[...]
Wenn man das ausführt, sieht man, dass alle o.a. Objekte als Instanzen von object aufgefasst werden, das gilt also auch für builtin-Typen. Ich will aber eigentlich gerade wissen, ob eine Variable eine Instanz irgendeines User-defined-Typs ist. instanceof könnte ich nur überprüfen, ob obj Instanz eines bestimmten Typen ist.
Ja weil du auch ``isinstance(foo, object)`` schreibst.

Wenn du die Klasse ``Foo`` und ``Bar`` hast , die beiden von dem obersten Objekt in der Hierarchie ``object`` abgeleitet sind, dann wird es für Instanzen von ``Foo`` und ``Bar`` immer ein ``True`` zurückliefern, wenn du ein ``isinstance`` auf ``object`` anwendest. Was du möchtest ist eigentlich zu testen ob die Instanz eine Instanz von ``Foo``, ``Bar``, etc ist.

Hier mal ein Beispiel zur Verdeutlichung:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Foo(object):
    pass

class Bar(object):
    pass

foo = Foo()
bar = Bar()

print "Ist ``foo`` eine Instanz von ``Foo``?: %s" % isinstance(foo, Foo)
print "Ist ``foo`` eine Instanz von ``Bar``?: %s" % isinstance(foo, Bar)
print "Ist ``foo`` eine Instanz von ``object``?: %s" % isinstance(foo, object)
print

print "Ist ``bar`` eine Instanz von ``Foo``?: %s" % isinstance(bar, Foo)
print "Ist ``bar`` eine Instanz von ``Bar``?: %s" % isinstance(bar, Bar)
print "Ist ``bar`` eine Instanz von ``object``?: %s" % isinstance(bar, object)
Analog dazu gilt das oben angesprochene für ``object``, auch für eine Ableitung von Foo:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Foo(object):
    pass

class SpecialFoo(Foo):
    pass

foo = SpecialFoo()

print "Ist ``foo`` eine Instanz von ``Foo``?: %s" % isinstance(foo, Foo)
print "Ist ``foo`` eine Instanz von ``SpecialFoo``?: %s" % isinstance(foo, SpecialFoo)
print "Ist ``foo`` eine Instanz von ``object``?: %s" % isinstance(foo, object)
Ist auch logisch so, da ``SpecialFoo` als Base ``foo`` hat, und es ein ``isinstance``, wie bei testen auf ``object``, mitberücksichtigt.



In deinem Beispiel müsstest du allso ``isinstance(td, TestDict)`` schreiben, um herauszubekommen ob ``td`` eine Instanz von ``TestDict`` ist.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

er will ja anscheinend nur wissen ob er einen builtin vor sich hat oder nicht ...
Zuletzt geändert von Dill am Freitag 9. März 2007, 15:04, insgesamt 1-mal geändert.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Dill hat geschrieben:er will ja anscheinen nur wissen ob er einen builtin vor sich hat oder nicht ...
Ah, ok. Moment.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

sape hat geschrieben:
Dill hat geschrieben:er will ja anscheinen nur wissen ob er einen builtin vor sich hat oder nicht ...
Ah, ok. Moment.
Hier:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import inspect

class Foo(object):
    pass

class SpecialFoo(Foo):
    pass


foo = Foo()

for name, ref in inspect.getmembers(__builtins__):
    if name != 'object':
        try:
            print "Ist ``foo`` eine Instanz von ``%s``: %s" % (
                name, 
                isinstance(foo, ref)
            )
        except TypeError, error:
            pass
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import inspect

def is_builtin(instance):
    for name, ref in inspect.getmembers(__builtins__):
        try:
            if isinstance(instance, ref) and name != 'object':
                return True
        except TypeError:
            pass
    return False


if __name__ == '__main__':
   class Foo(object):
       pass
   
   foo = Foo()
   str_ = "Hello World!"
   
   print "ist foo ein Builtin?: %s" % is_builtin(foo)
   print "ist str_ ein Builtin?: %s" % is_builtin(str_)
   print
   # Geht nicht ;)
   print "ist str_ ein Builtin?: %s" % inspect.isbuiltin(str_)

Code: Alles auswählen

ist foo ein Builtin?: False
ist str_ ein Builtin?: True

ist str_ ein Builtin?: False
Edit: Korrektur.
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hi,

vielen Dank an alle, die geantwortet haben, insbesondere Dill und sape, die mir klargemacht haben, dass ich nicht nach einer Funktion suchen muss, die mir sagt, ob ein Objekt eine Instanz irgendeines benutzerdefinierten Typs ist, sondern dass es reicht, zu gucken, ob der Typ keiner der Builtins ist. Hätte ich vielleicht auch selbst drauf kommen können :D

Gruss

Wolfgang
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Verrätst du uns auch noch, wofür du das brauchst?

Mir scheint es so, als ob du irgendwie Ducktyping umgehen willst und z.B. keine selbtdefinierten Objekte in deine Listen lassen willst, was i. d. R. keine gute Idee ist.
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hi,

da hier mehrfach gefragt wurde, wofür ich das brauche:
ich brauchte Code, der zwei Argumente auf Wertegleichheit vergleicht, wobei die Argumente primär Dictionaries sind, aber auch Objekte von Klassen sein können, die "das Dictionary-Interface" implementieren, da es nur um Lesezugriff geht, __getitem__(), has_key(), keys() und items(). Dictionaries können ja wiederum Dictionaries als Werter enthalten. Die Behandlung von solchen Werten bietet sich also als rekursive Implementierung an. Beim Vergleich der Werte muss ich auf jeden Fall unterscheiden zwischen builtins, die ich einfach (==) vergleichen kann, und Dictionaries bzw. Objekten, die die entsprechende Schnittstelle implementieren:

Code: Alles auswählen

def compareDicts(dict1, dict2):
    retVal = True
    keys1 = dict1.keys()
    keys1.sort()
    keys2 = dict2.keys()
    keys2.sort()
    if keys1 <> keys2:
        retVal = False
    else:
        for k in keys1:
            val1 = dict1[k]
            val2 = dict2[k]
            if implements_getitem(val1):
                if not dictEq(val1, val2):
                    retVal =  False
                    break
            else:
                if val1 <> val2:
                    retVal =  False
                    break
    return retVal 
implements_getitem wollte ich ursprünglich so implementieren, dass es prüft, ob das Objekt vom Typ Dictionary ist, dann gibt es __getitem__, oder ob es ein selbstdefinierter Type ist, der ein Attribut __getitem__ hat, das dann noch vom Type Instanz-Methode sein sollte.
Inzwischen habe ich implements_getitem anders implmentiert: Ich versuche __getitem__ mit einem Dummy-Schlüssel auf dem Objekt aufzurufen, wenn's klappt, gibt's offensichtlich __getitem__, wenn's eine Exception gibt, gibt's das nicht ...

Soweit zur Frage, was ich eigentlich bezwecken wollte.

Gruss

Wolfgang
BlackJack

Ich habe mich auch mal dran versucht, allerdings ungetestet:

Code: Alles auswählen

from itertools import imap


def all(iterable):
    return False not in imap(bool, iterable)


def compare_mappings(mapping_a, mapping_b):
    """Recursivly compares two mappings.
    
    Each mapping type must at least implement `__getitem__()` and `keys()`.
    """
    # 
    # First try to get the keys of both objects and put them into a `set` for
    # easy comparison.
    # 
    # A `set` is used instead of a sorted `list` because keys just have to be
    # hashable but don't need to have an order defined.  For example it's
    # possible to use `complex` objects as keys, but not to sort them.
    # 
    # If one of the objects doesn't have a `keys` attribute, or one that is
    # not callable, do a "normal" comparison.
    # 
    try:
        keys_a = set(mapping_a.keys())
        keys_b = set(mapping_b.keys())
    except (AttributeError, TypeError):
        return mapping_a == mapping_b
    
    if keys_a == keys_b:
        return all(compare_mappings(mapping_a[key], mapping_b[key])
                   for key in keys_a)


class TestMap(object):
    def __init__(self, items):
        self.data = dict(items)
    
    def __getitem__(self, key):
        return self.data[key]
    
    def keys(self):
        return self.data.keys()


def main():
    a = {'a': {'b': 'c'}}
    b = TestMap([('a', TestMap([('b', 'c')]))])
    for x, y in ((a, a), (a, b), (b, b)):
        print compare_mappings(x, y)
Mehr als den kleinen Testcode in der `main()` habe ich nicht ausgeführt. Mindestens über die `set()`\s solltest Du aber mal nachdenken.
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hallo BlackJack,

das sieht natürlich eleganter aus. Allerdings benutzt es leider einige Sprachfeatures, die in Python2.2 noch nicht verfügbar sind. Auf jeden Fall wird das wohl ganz interessant, was man alles mit Python wird machen können, wenn wir hier erstmal auf 2.4 oder 2.5 umsteigen.

Schöne Anregung, sich damit in Zukunft zu beschäftigen.

Danke

Wolfgang
Antworten