简体   繁体   中英

Is using Python `isinstance` ever right?

I've got a 2D array of different blocks, all inheriting from Block. I want to check if the block that I clicked on is a Dirt type block, like this:

clickedblock = getClickedBlock()

if isinstance(clickedblock, Dirt):
    place a block

else:
    don't place a block

I've heard that isinstance is bad, and should be avoided because it creates forks in code. What times would isinstance be good to use?

Another more cumbersome solution for my problem would be to have a field of Block called 'id' and then check if it equals some constant that means Dirt. But that sounds quite bad and more prone for mistake than the simple isinstance .

Your example seems like a legitimate use case of isinstance() .

It's not that isinstance() is bad, often polymorphism can be used for the same purpose (which results in cleaner code in where the class is used).

But sometimes, isinstance() is what you need. For example, the pythonic way of detecting whether a variable is string or not is isinstance(var, basestring) .

I learned the hard way against using it. The problem is that the outcome is sensitive to how the class definitions were imported:

  • in the place where the object was instantiated
  • in the place where the isinstance test is performed

If one import was relative and the other absolute - the check will fail. Essentially it'll be like checking on equality between SomeClass vs somepackage.SomeClass . It doesn't even matter that they come from the same file and so on. Also, there will be a similar outcome if you somehow have both the root directory and somepackage directory in your PYTHONPATH - then an absolute-styled import could denote a path from either of the "source roots" so two different absolute-styled imports would result in failing instance checks.

One could argue about how good practices would anyway prevent that from happening but good practices are also largely about not taking chances. In that spirit I prefer to put some abstract method in a common ancestor class so that I can later rely on how things quack rather than on what the interpreter believes them to be.

In Java each class resolves to its fully qualified class name. These are unique within a program and tests for instances are a breeze. In Python these can be slippery.

I think I'd change it to be more like:

PLACEABLE_TYPES = [ Dirt ]
if isinstance(clickedblock, PLACEABLE_TYPES):
   place the block
else:
   don't place the block

but the idea in the comments of:

if clickedblock.is_placeable(that_place):
    place the block
else:
    don't place the block

also has merit.

If you don't want to use it, you've got other options. Traditional duck typing solution:

try:
    clickedblock_place = clickedblock.place
except AttributeError:
    # don't place block
else:
    clickedblock_place()

Or you can use hasattr :

if hasattr(clickedblock, 'place'):
    clickedblock.place()

I hardly ever use isinstance except for checking up (or is it down?) the inheritance hierarchy, say, for instance, if you need to know if a name points to a str OR a unicode :

if isinstance(str1, basestring):
    blah, blah, blah

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM