fastest way to detect a user type

Steven D'Aprano steve at pearwood.info
Sun Feb 1 03:15:58 EST 2009


Robin Becker wrote:

> Whilst considering a port of old code to python 3 I see that in several
> places we are using type comparisons to control processing of user
> instances (as opposed to instances of built in types eg float, int, str)
> 
> I find that the obvious alternatives are not as fast as the current
> code; func0 below. On my machine isinstance seems slower than type for
> some reason. My 2.6 timings are

First question is, why do you care that it's slower? The difference between
the fastest and slowest functions is 1.16-0.33 = 0.83 microsecond. If you
call the slowest function one million times, your code will run less than a
second longer.

Does that really matter, or are you engaged in premature optimization? In
your test functions, the branches all execute "pass". Your real code
probably calls other functions, makes calculations, etc, which will all
take time. Probably milliseconds rather than microseconds. I suspect you're
concerned about a difference of 0.1 of a percent, of one small part of your
entire application. Unless you have profiled your code and this really is a
bottleneck, I recommend you worry more about making your code readable and
maintainable than worrying about micro-optimisations.

Even more important that being readable is being *correct*, and I believe
that your code has some unexpected failure modes (bugs). See below:



> so func 3 seems to be the fastest option for the case when the first
> test matches, but is poor when it doesn't. Can anyone suggest a better
> way to determine if an object is a user instance?
> 
> ##############################
> from types import InstanceType

I believe this will go away in Python 3, as all classes will be New Style
classes.


> class X:
>      __X__=True

This is an Old Style class in Python 2.x, and a New Style class in Python 3.

Using hasattr('__X__') is a curious way of detecting what you want. I
suppose it could be argued that it is a variety of duck-typing: "if it has
a duck's bill, it must be a duck". (Unless it is a platypus, of course.)
However, attribute names with leading and trailing double-underscores are
reserved for use as "special methods". You should rename it to something
more appropriate: _MAGIC_LABEL, say.

 
> class V(X):
>      pass
> 
> def func0(ob):
>      t=type(ob)
>      if t is InstanceType:
>          pass

This test is too broad. It will succeed for *any* old-style class, not just
X and V instances. That's probably not what you want.

It will also fail if ob is an instance of a New Style class. Remember that
in Python 3, all classes become new-style.


>      elif t in (float, int):
>          pass

This test will fail if ob is a subclass of float or int. That's almost
certainly the wrong behavior. A better way of writing that is:

    elif issubclass(t, (float, int)):
        pass


>      else:
>          pass
> 
> def func1(ob):
>      if isinstance(ob,X):
>          pass

If you have to do type checking, that's the recommended way of doing so.



>      elif type(ob) in (float, int):
>          pass

The usual way to write that is:

    if isinstance(ob, (float, int)):
        pass



Hope this helps,


-- 
Steven




More information about the Python-list mailing list