
Base classes and polymorphism
In this section, we'll flirt with the idea of Pretty Poor Polymorphism. Inspection of argument values is a Python programming practice that should be isolated to a few special cases.
Well-done polymorphism follows what is sometimes called the Liskov Substitution Principle. Polymorphic classes can be used interchangeably. Each polymorphic class has the same suite of properties. For more information, visit http://en.wikipedia.org/wiki/Liskov_substitution_principle.
Overusing isinstance()
to distinguish between the types of arguments can lead to a needlessly complex (and slow) program. Instance comparisons are made all the time, but errors are generally only introduced through software maintenance. Unit testing is a far better way to find programming errors than verbose type-checking in the code.
Method functions with lots of isinstance()
methods can be a symptom of a poor (or incomplete) design of polymorphic classes. Rather than having type-specific processing outside of a class definition, it's often better to extend or wrap classes to make them more properly polymorphic and encapsulate the type-specific processing within the class definition.
One good use of the isinstance()
method is to create diagnostic messages. A simple approach is to use the assert
statement:
assert isinstance( some_argument, collections.abc.Container ), "{0!r} not a Container".format(some_argument)
This will raise an AssertionError
exception to indicate that there's a problem. This has the advantage that it is short and to the point. However, it has two disadvantages: assertions can be silenced, and it would probably be better to raise a TypeError
for this. The following example might be better:
if not isinstance(some_argument, collections.abc.Container): raise TypeError( "{0!r} not a Container".format(some_argument) )
The preceding code has the advantage that it raises the correct error. However, it has the disadvantage that it is long winded.
The more Pythonic approach is summarized as follows:
"It's better to ask for forgiveness than to ask for permission."
This is generally taken to mean that we should minimize the upfront testing of arguments (asking permission) to see if they're the correct type. Argument-type inspections are rarely of any tangible benefit. Instead, we should handle the exceptions appropriately (asking forgiveness).
What's best is to combine diagnostic information with the exception in the unlikely event that an inappropriate type is used and somehow passed through unit testing into operation.
The following is often what's done:
try: found = value in some_argument except TypeError: if not isinstance(some_argument, collections.abc.Container): warnings.warn( "{0!r} not a Container".format(some_argument) ) raise
The isinstance()
method assumes that some_argument
is a proper instance of a collections.abc.Container
class and will respond to the in
operator.
In the unlikely event that someone changes the application and some_argument
is now of the wrong class, the application will write a diagnostic message and crash with a TypeError
exception.