Python: can isinstance(i, type(i)) evaluate to Fal

2019-07-18 13:05发布

问题:

I am looking for something in the following piece of code so that the isinstance() check afterwards evaluates to True in any case:

i = WonderfulClass()
classinfo_of_i = something(i)
isinstance(i, classinfo_of_i) # must evaluate to True

If type is your answer, I'd be grateful if you explained why. Is type the real counterpart of isinstance? Or, asked the other way round, can you think of a case where isinstance(i, type(i)) evaluates to False?

This question arose in the context of the simple way to check if the elements of a list or set are single type?, where we have to go through a sequence and check if all the sequence elements are of the same kind. In this context, elements will be compared to each other. This comparison could be based on type or based on isinstance.

Relevant documentation regarding isinstance(object, classinfo):

Return true if the object argument is an instance of the classinfo argument

回答1:

"Is type the real counterpart of isinstance? Or, asked the other way round, can you think of a case where isinstance(i, type(i)) evaluates to False?"

I can't see any situation where isinstance(i, type(i)) would not return True.

type() inspects and returns the type object from an instance so there's no reason why the returned value would fail the isinstance() check.

Digging into the source of cPython, we see that the code behind type() simply returns the type object attached to the instance:

v = (PyObject *)o->ob_type;
Py_INCREF(v);
return v;

while the first thing that the isintance() code does is check if the types match exactly (it would later move on to match against classes in the chain of inheritance):

int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
   _Py_IDENTIFIER(__instancecheck__);
   PyObject *checker;

   /* Quick test for an exact match */
   if (Py_TYPE(inst) == (PyTypeObject *)cls)
      return 1;

Note that Py_TYPE is simply a macro that returns obj->ob_type which matches the return value of type(). This is defined in Include/object.h as:

#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)


回答2:

type() returns what is expected, but only if you use new style classes (inherit from object).

Consider the following:

>>> class WonderfulClass(object):
...     pass
...
>>> i = WonderfulClass()
>>> isinstance(i,type(i))
True
>>> type(i)
<class '__main__.WonderfulClass'>
>>> class AnotherClass:
...     pass
...
>>> z = AnotherClass()
>>> type(z)
<type 'instance'>
>>> isinstance(z,type(z))
True

So although the isinstance check will work, its worth noting the differences.



回答3:

First of all, isinstance is meant to deal with inheritance. E.g.

>>> isinstance("asdfs", object)
True

Here a string was considered an instance of object, as "str" type is a heir of "object" type. So type is not a strict counterpart to isinstance.

Everything is an object in python, so, isinstance(anything, object) should return True for anything and, thus, all the values in your array have a common ancestor in their class hierarchy: object.

If you'd like to directly refer to a more special type/store it in a variable and be able to compare to it, types module might be of interest to you.



回答4:

>>> class P:
...   pass
...
>>> p = P()
>>> type(p)
<type 'instance'>
>>> p.__class__
<class __main__.P at 0x015A6A78>

>>> class D(object):
...   pass
...
>>>
>>> d = D()
>>> type(d)
<class '__main__.D'>
>>> d.__class__
<class '__main__.D'>