I have a class named Factor in the module Factor.py (https://github.com/pgmpy/pgmpy/blob/dev/pgmpy/factors/Factor.py) and also have function named factor_product in Factor.py as:
def factor_product(*args):
if not all(isinstance(phi, Factor) for phi in args):
raise TypeError("Input parameters must be factors")
return functools.reduce(lambda phi1, phi2: _bivar_factor_operation(phi1, phi2,
operation='M'), args)
Now if I even pass instances of Factor to the function, it still throws TypeError. A few lines from the debugger with breakpoint set just above the if statement:
(Pdb) args
args = (<pgmpy.factors.Factor.Factor object at 0x7fed0faf76a0>, <pgmpy.factors.Factor.Factor object at 0x7fed0faf7da0>)
(Pdb) isinstance(args[0], Factor)
False
(Pdb) type(args[0])
<class 'pgmpy.factors.Factor.Factor'>
(Pdb) Factor
<class 'pgmpy.factors.Factor.Factor'>
Any idea why this is happening ?
reload is a good way to end up with two copies of the same class from the same module: one from before the reload (if any instances of that class are still lurking about) and one from after.
Most likely you had objects of the new type, but Factor referred to the old type, since it was imported some time ago. So it's completely true that your objects aren't instances of Factor... not that Factor, anyway.
Never trust reload. :)
As isinstance Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof , it just return true if you pass the instance of your class to it not the class itself , see the following example :
>>> class A :
... pass
...
>>> isinstance(A,A)
False
>>> isinstance(A(),A)
True
>>> z=A()
>>> isinstance(z,A)
True
Related
So disclaimer: this question has piqued my curiosity a bit, and I'm asking this for purely educational purposes. More of a challenge for the Python gurus here I suppose!
Is it possible to make the output of type(foo) return a different value than the actual instance class? i.e. can it pose as an imposter and pass a check such as type(Foo()) is Bar?
#juanpa.arrivillaga made a suggestion of manually re-assigning __class__ on the instance, but that has the effect of changing how all other methods would be called. e.g.
class Foo:
def test(self):
return 1
class Bar:
def test(self):
return 2
foo = Foo()
foo.__class__ = Bar
print(type(foo) is Bar)
print(foo.test())
>>> True
>>> 2
The desired outputs would be True, 1. i.e The class returned in type is different than the instance, and the instance methods defined in the real class still get invoked.
No - the __class__ attribute is a fundamental information on the layout of all Python objects as "seen" on the C API level itself. And that is what is checked by the call to type.
That means: every Python object have a slot in its in-memory layout with space for a single pointer, to the Python object that is that object's class.
Even if you use ctypes or other means to override protection to that slot and change it from Python code (since modifying obj.__class__ with = is guarded at the C level), changing it effectively changes the object type: the value in the __class__ slot IS the object's class, and the test method would be picked from the class in there (Bar) in your example.
However there is more information here: in all documentation, type(obj) is regarded as equivalent as obj.__class__ - however, if the objects'class defines a descriptor with the name __class__, it is used when one uses the form obj.__class__. type(obj) however will check the instance's __class__ slot directly and return the true class.
So, this can "lie" to code using obj.__class__, but not type(obj):
class Bar:
def test(self):
return 2
class Foo:
def test(self):
return 1
#property
def __class__(self):
return Bar
Property on the metaclass
Trying to mess with creating a __class__ descriptor on the metaclass of Foo itself will be messy -- both type(Foo()) and repr(Foo()) will report an instance of Bar, but the "real" object class will be Foo. In a sense, yes, it makes type(Foo()) lie, but not in the way you were thinking about - type(Foo()) will output the repr of Bar(), but it is Foo's repr that is messed up, due to implementation details inside type.__call__:
In [73]: class M(type):
...: #property
...: def __class__(cls):
...: return Bar
...:
In [74]: class Foo(metaclass=M):
...: def test(self):
...: return 1
...:
In [75]: type(Foo())
Out[75]: <__main__.Bar at 0x55665b000578>
In [76]: type(Foo()) is Bar
Out[76]: False
In [77]: type(Foo()) is Foo
Out[77]: True
In [78]: Foo
Out[78]: <__main__.Bar at 0x55665b000578>
In [79]: Foo().test()
Out[79]: 1
In [80]: Bar().test()
Out[80]: 2
In [81]: type(Foo())().test()
Out[81]: 1
Modifying type itself
Since no one "imports" type from anywhere, and just use
the built-in type itself, it is possible to monkeypatch the builtin
type callable to report a false class - and it will work for all
Python code in the same process relying on the call to type:
original_type = __builtins__["type"] if isinstance("__builtins__", dict) else __builtins__.type
def type(obj_or_name, bases=None, attrs=None, **kwargs):
if bases is not None:
return original_type(obj_or_name, bases, attrs, **kwargs)
if hasattr(obj_or_name, "__fakeclass__"):
return getattr(obj_or_name, "__fakeclass__")
return original_type(obj_or_name)
if isinstance(__builtins__, dict):
__builtins__["type"] = type
else:
__builtins__.type = type
del type
There is one trick here I had not find in the docs: when acessing __builtins__ in a program, it works as a dictionary. However, in an interactive environment such as Python's Repl or Ipython, it is a
module - retrieving the original type and writting the modified
version to __builtins__ have to take that into account - the code above
works both ways.
And testing this (I imported the snippet above from a .py file on disk):
>>> class Bar:
... def test(self):
... return 2
...
>>> class Foo:
... def test(self):
... return 1
... __fakeclass__ = Bar
...
>>> type(Foo())
<class '__main__.Bar'>
>>>
>>> Foo().__class__
<class '__main__.Foo'>
>>> Foo().test()
1
Although this works for demonstration purposes, replacing the built-in type caused "dissonances" that proved fatal in a more complex environment such as IPython: Ipython will crash and terminate immediately if the snippet above is run.
This question already has answers here:
What's the canonical way to check for type in Python?
(15 answers)
Closed 6 months ago.
Is there a simple way to determine if a variable is a list, dictionary, or something else?
There are two built-in functions that help you identify the type of an object. You can use type() if you need the exact type of an object, and isinstance() to check an object’s type against something. Usually, you want to use isinstance() most of the times since it is very robust and also supports type inheritance.
To get the actual type of an object, you use the built-in type() function. Passing an object as the only parameter will return the type object of that object:
>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True
This of course also works for custom types:
>>> class Test1 (object):
pass
>>> class Test2 (Test1):
pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True
Note that type() will only return the immediate type of the object, but won’t be able to tell you about type inheritance.
>>> type(b) is Test1
False
To cover that, you should use the isinstance function. This of course also works for built-in types:
>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True
isinstance() is usually the preferred way to ensure the type of an object because it will also accept derived types. So unless you actually need the type object (for whatever reason), using isinstance() is preferred over type().
The second parameter of isinstance() also accepts a tuple of types, so it’s possible to check for multiple types at once. isinstance will then return true, if the object is of any of those types:
>>> isinstance([], (tuple, list, set))
True
Use type():
>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
It might be more Pythonic to use a try...except block. That way, if you have a class which quacks like a list, or quacks like a dict, it will behave properly regardless of what its type really is.
To clarify, the preferred method of "telling the difference" between variable types is with something called duck typing: as long as the methods (and return types) that a variable responds to are what your subroutine expects, treat it like what you expect it to be. For example, if you have a class that overloads the bracket operators with getattr and setattr, but uses some funny internal scheme, it would be appropriate for it to behave as a dictionary if that's what it's trying to emulate.
The other problem with the type(A) is type(B) checking is that if A is a subclass of B, it evaluates to false when, programmatically, you would hope it would be true. If an object is a subclass of a list, it should work like a list: checking the type as presented in the other answer will prevent this. (isinstance will work, however).
On instances of object you also have the:
__class__
attribute. Here is a sample taken from Python 3.3 console
>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
... pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>
Beware that in python 3.x and in New-Style classes (aviable optionally from Python 2.6) class and type have been merged and this can sometime lead to unexpected results. Mainly for this reason my favorite way of testing types/classes is to the isinstance built in function.
Determine the type of a Python object
Determine the type of an object with type
>>> obj = object()
>>> type(obj)
<class 'object'>
Although it works, avoid double underscore attributes like __class__ - they're not semantically public, and, while perhaps not in this case, the builtin functions usually have better behavior.
>>> obj.__class__ # avoid this!
<class 'object'>
type checking
Is there a simple way to determine if a variable is a list, dictionary, or something else? I am getting an object back that may be either type and I need to be able to tell the difference.
Well that's a different question, don't use type - use isinstance:
def foo(obj):
"""given a string with items separated by spaces,
or a list or tuple,
do something sensible
"""
if isinstance(obj, str):
obj = str.split()
return _foo_handles_only_lists_or_tuples(obj)
This covers the case where your user might be doing something clever or sensible by subclassing str - according to the principle of Liskov Substitution, you want to be able to use subclass instances without breaking your code - and isinstance supports this.
Use Abstractions
Even better, you might look for a specific Abstract Base Class from collections or numbers:
from collections import Iterable
from numbers import Number
def bar(obj):
"""does something sensible with an iterable of numbers,
or just one number
"""
if isinstance(obj, Number): # make it a 1-tuple
obj = (obj,)
if not isinstance(obj, Iterable):
raise TypeError('obj must be either a number or iterable of numbers')
return _bar_sensible_with_iterable(obj)
Or Just Don't explicitly Type-check
Or, perhaps best of all, use duck-typing, and don't explicitly type-check your code. Duck-typing supports Liskov Substitution with more elegance and less verbosity.
def baz(obj):
"""given an obj, a dict (or anything with an .items method)
do something sensible with each key-value pair
"""
for key, value in obj.items():
_baz_something_sensible(key, value)
Conclusion
Use type to actually get an instance's class.
Use isinstance to explicitly check for actual subclasses or registered abstractions.
And just avoid type-checking where it makes sense.
You can use type() or isinstance().
>>> type([]) is list
True
Be warned that you can clobber list or any other type by assigning a variable in the current scope of the same name.
>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'
Above we see that dict gets reassigned to a string, therefore the test:
type({}) is dict
...fails.
To get around this and use type() more cautiously:
>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
be careful using isinstance
isinstance(True, bool)
True
>>> isinstance(True, int)
True
but type
type(True) == bool
True
>>> type(True) == int
False
While the questions is pretty old, I stumbled across this while finding out a proper way myself, and I think it still needs clarifying, at least for Python 2.x (did not check on Python 3, but since the issue arises with classic classes which are gone on such version, it probably doesn't matter).
Here I'm trying to answer the title's question: how can I determine the type of an arbitrary object? Other suggestions about using or not using isinstance are fine in many comments and answers, but I'm not addressing those concerns.
The main issue with the type() approach is that it doesn't work properly with old-style instances:
class One:
pass
class Two:
pass
o = One()
t = Two()
o_type = type(o)
t_type = type(t)
print "Are o and t instances of the same class?", o_type is t_type
Executing this snippet would yield:
Are o and t instances of the same class? True
Which, I argue, is not what most people would expect.
The __class__ approach is the most close to correctness, but it won't work in one crucial case: when the passed-in object is an old-style class (not an instance!), since those objects lack such attribute.
This is the smallest snippet of code I could think of that satisfies such legitimate question in a consistent fashion:
#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
obj_type = getattr(obj, "__class__", _NO_CLASS)
if obj_type is not _NO_CLASS:
return obj_type
# AFAIK the only situation where this happens is an old-style class
obj_type = type(obj)
if obj_type is not ClassType:
raise ValueError("Could not determine object '{}' type.".format(obj_type))
return obj_type
using type()
x='hello this is a string'
print(type(x))
output
<class 'str'>
to extract only the str use this
x='this is a string'
print(type(x).__name__)#you can use__name__to find class
output
str
if you use type(variable).__name__ it can be read by us
In many practical cases instead of using type or isinstance you can also use #functools.singledispatch, which is used to define generic functions (function composed of multiple functions implementing the same operation for different types).
In other words, you would want to use it when you have a code like the following:
def do_something(arg):
if isinstance(arg, int):
... # some code specific to processing integers
if isinstance(arg, str):
... # some code specific to processing strings
if isinstance(arg, list):
... # some code specific to processing lists
... # etc
Here is a small example of how it works:
from functools import singledispatch
#singledispatch
def say_type(arg):
raise NotImplementedError(f"I don't work with {type(arg)}")
#say_type.register
def _(arg: int):
print(f"{arg} is an integer")
#say_type.register
def _(arg: bool):
print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>
Additionaly we can use abstract classes to cover several types at once:
from collections.abc import Sequence
#say_type.register
def _(arg: Sequence):
print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!
As an aside to the previous answers, it's worth mentioning the existence of collections.abc which contains several abstract base classes (ABCs) that complement duck-typing.
For example, instead of explicitly checking if something is a list with:
isinstance(my_obj, list)
you could, if you're only interested in seeing if the object you have allows getting items, use collections.abc.Sequence:
from collections.abc import Sequence
isinstance(my_obj, Sequence)
if you're strictly interested in objects that allow getting, setting and deleting items (i.e mutable sequences), you'd opt for collections.abc.MutableSequence.
Many other ABCs are defined there, Mapping for objects that can be used as maps, Iterable, Callable, et cetera. A full list of all these can be seen in the documentation for collections.abc.
value = 12
print(type(value)) # will return <class 'int'> (means integer)
or you can do something like this
value = 12
print(type(value) == int) # will return true
type() is a better solution than isinstance(), particularly for booleans:
True and False are just keywords that mean 1 and 0 in python. Thus,
isinstance(True, int)
and
isinstance(False, int)
both return True. Both booleans are an instance of an integer. type(), however, is more clever:
type(True) == int
returns False.
In general you can extract a string from object with the class name,
str_class = object.__class__.__name__
and using it for comparison,
if str_class == 'dict':
# blablabla..
elif str_class == 'customclass':
# blebleble..
For the sake of completeness, isinstance will not work for type checking of a subtype that is not an instance. While that makes perfect sense, none of the answers (including the accepted one) covers it. Use issubclass for that.
>>> class a(list):
... pass
...
>>> isinstance(a, list)
False
>>> issubclass(a, list)
True
Can magic methods be overridden outside of a class?
When I do something like this
def __int__(x):
return x + 5
a = 5
print(int(a))
it prints '5' instead of '10'. Do I do something wrong or magic methods just can't be overridden outside of a class?
Short answer; not really.
You cannot arbitrarily change the behaviour of int() a builtin function (*which internally calls __int__()) on arbitrary builtin types such as int(s).
You can however change the behaviour of custom objects like this:
Example:
class Foo(object):
def __init__(self, value):
self.value = value
def __add__(self, other):
self.value += other
def __repr__(self):
return "<Foo(value={0:d})>".format(self.value)
Demo:
>>> x = Foo(5)
>>> x + 5
>>> x
<Foo(value=10)>
This overrides two things here and implements two special methods:
__repr__() which get called by repr()
__add__() which get called by the + operator.
Update: As per the comments above; techincally you can redefine the builtin function int; Example:
def int(x):
return x + 5
int(5) # returns 10
However this is not recommended and does not change the overall behaviour of the object x.
Update #2: The reason you cannot change the behaviour of bultin types (without modifying the underlying source or using Cuthon or ctypes) is because builtin types in Python are not exposed or mutable to the user unlike Homoiconic Languages (See: Homoiconicity). -- Even then I'm not really sure you can with Cython/ctypes; but the reason question is "Why do you want to do this?"
Update #3: See Python's documentation on Data Model (object.__complex__ for example).
You can redefine a top-level __int__ function, but nobody ever calls that.
As implied in the Data Model documentation, when you write int(x), that calls x.__int__(), not __int__(x).
And even that isn't really true. First, __int__ is a special method, meaning it's allowed to call type(x).__int__(x) rather than x.__int__(), but that doesn't matter here. Second, it's not required to call __int__ unless you give it something that isn't already an int (and call it with the one-argument form). So, it could be as if it's was written like this:
def int(x, base=None):
if base is not None:
return do_basey_stuff(x, base)
if isinstance(x, int):
return x
return type(x).__int__(x)
So, there is no way to change what int(5) will do… short of just shadowing the builtin int function with a different builtin/global/local function of the same name, of course.
But what if you wanted to, say, change int(5.5)? That's not an int, so it's going to call float.__int__(5.5). So, all we have to do is monkeypatch that, right?
Well, yes, except that Python allows builtin types to be immutable, and most of the builtin types in CPython are. So, if you try it:
>>> _real_float_int = float.__int__
>>> def _float_int(self):
... return _real_float_int(self) + 5
>>> _float_int(5.5)
10
>>> float.__int__ = _float_int
TypeError: can't set attributes of built-in/extension type 'float'
However, if you're defining your own types, that's a different story:
>>> class MyFloat(float):
... def __int__(self):
... return super().__int__() + 5
>>> f = MyFloat(5.5)
>>> int(f)
10
Is it possible in python to check if an object is a class object. IE if you have
class Foo(object):
pass
How could you check if o is Foo (or some other class) or an instance of Foo (or any other class instance)? In Java this would be a simple matter. Just check if the object is an instance of Class. Is there something similar in python or are you supposed to just not care?
Slight clarification: I'm trying to make a function that prints information about the parameter its given. So if you pass in o, where o = Foo() it prints out information about Foo. If you pass in Foo it should print out the exact same information. Not information about Type.
Use the isinstance builtin function.
>>> o = Foo()
>>> isinstance(o, Foo)
True
>>> isinstance(13, Foo)
False
This also works for subclasses:
>>> class Bar(Foo): pass
>>> b = Bar()
>>> isinstance(b, Foo)
True
>>> isinstance(b, Bar)
True
Yes, normally, you are supposed to not particularly care what type the object is. Instead, you just call the method you want on o so that people can plug in arbitrary objects that conform to your interface. This wouldn't be possible if you were to aggressively check the types of objects that you're using. This principle is called duck typing, and allows you a bit more freedom in how you choose to write your code.
Python is pragmatic though, so feel free to use isinstance if it makes sense for your particular program.
Edit:
To check if some variable is a class vs an instance, you can do this:
>>> isinstance(Foo, type) # returns true if the variable is a type.
True
>>> isinstance(o, type)
False
My end goal is to make a function that prints out information about an object if its an instance and print something different if its a class. So this time I do care.
First, understand that classes are instances — they're instances of type:
>>> class Foo(object):
... pass
...
>>> isinstance(Foo, type)
True
So, you can pick out classes that way, but keep in mind that classes are instances too. (And thus, you can pass classes to functions, return them from functions store them in lists, create the on the fly…)
the isinstance() function
isinstance(o, Foo)
and you can also use it to compare o to object
In [18]: class Foo(object): pass
In [20]: o_instance = Foo()
In [21]: o_class = Foo
In [22]: isinstance(o_instance, Foo)
Out[22]: True
In [23]: isinstance(o_class, Foo)
Out[23]: False
In [24]: isinstance(o_instance, object)
Out[24]: True
In [25]: isinstance(o_class, object)
Out[25]: True
I had to do like Thanatos said and check
isinstance(Foo, type)
But in the case of old class types you have to also do
isinstance(Foo, types.ClassType)
This question already has answers here:
What are metaclasses in Python?
(25 answers)
Closed 7 years ago.
I came upon this reading the python documentation on the super keyword:
If the second argument is omitted, the super object returned is unbound. If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true (this is useful for classmethods).
Can someone please give me an example of a distinction between passing a Type as a second argument versus passing an Object?
Is the documentation talking about an instance of an object?
Thank you.
Python's super function does different things depending on what it's arguments are. Here's a demonstration of different ways of using it:
class Base(object):
def __init__(self, val):
self.val = val
#classmethod
def make_obj(cls, val):
return cls(val+1)
class Derived(Base):
def __init__(self, val):
# In this super call, the second argument "self" is an object.
# The result acts like an object of the Base class.
super(Derived, self).__init__(val+2)
#classmethod
def make_obj(cls, val):
# In this super call, the second argument "cls" is a type.
# The result acts like the Base class itself.
return super(Derived, cls).make_obj(val)
Test output:
>>> b1 = Base(0)
>>> b1.val
0
>>> b2 = Base.make_obj(0)
>>> b2.val
1
>>> d1 = Derived(0)
>>> d1.val
2
>>> d2 = Derived.make_obj(0)
>>> d2.val
3
The 3 result is the combination of the previous modifiers: 1 (from Base.make_obj) plus 2 (from Derived.__init__).
Note that it is possible to call super with just one argument to get an "unbound" super object, it is apparently not useful for much. There's not really any reason to do this unless you want to mess around with Python internals and you really know what you're doing.
In Python 3, you can also call super with no arguments (which is equivalent to the calls in the functions above, but more magical).
Object can be any Python class instance which may or may not be user defined. But,
when you are talking about a type, it refers to the default objects/collections like a list/tuple/dict/int/str etc.
Here is a simple exploration of the two functions. I found it illuminating going through this exercise. I often will create a simple program exploring the ins and outs of simple functions and save them for reference:
#
# Testing isinstance and issubclass
#
class C1(object):
def __init__(self):
object.__init__(self)
class B1(object):
def __init__(self):
object.__init__(self)
class B2(B1):
def __init__(self):
B1.__init__(self)
class CB1(C1,B1):
def __init__(self):
# not sure about this for multiple inheritance
C1.__init__(self)
B1.__init__(self)
c1 = C1()
b1 = B1()
cb1 = CB1()
def checkInstanceType(c, t):
if isinstance(c, t):
print c, "is of type", t
else:
print c, "is NOT of type", t
def checkSubclassType(c, t):
if issubclass(c, t):
print c, "is a subclass of type", t
else:
print c, "is NOT a subclass of type", t
print "comparing isinstance and issubclass"
print ""
# checking isinstance
print "checking isinstance"
# can check instance against type
checkInstanceType(c1, C1)
checkInstanceType(c1, B1)
checkInstanceType(c1, object)
# can check type against type
checkInstanceType(C1, object)
checkInstanceType(B1, object)
# cannot check instance against instance
try:
checkInstanceType(c1, b1)
except Exception, e:
print "failed to check instance against instance", e
print ""
# checking issubclass
print "checking issubclass"
# cannot check instance against type
try:
checkSubclassType(c1, C1)
except Exception, e:
print "failed to check instance against type", e
# can check type against type
checkSubclassType(C1, C1)
checkSubclassType(B1, C1)
checkSubclassType(CB1, C1)
checkSubclassType(CB1, B1)
# cannot check type against instance
try:
checkSubclassType(C1, c1)
except Exception, e:
print "failed to check type against instance", e
Edit:
Also consider the following as isinstance can break API implementations. An example would be an object that acts like a dictionary, but is not derived from dict. isinstance might check that an object is a dictionary, even though the object supports dictionary style access:
isinstance considered harmful
Edit2:
Can someone please give me an example of a distinction between passing a Type as a second argument versus passing an Object?
After testing the above code it tells me the second parameter must be a type. So in the following case:
checkInstanceType(c1, b1)
The call will fail. It could be written:
checkInstanceType(c1, type(b1))
So if you want to check the type of one instance against another instance you have to use the type() builtin call.