Base classes and polymorphism

In this section, we'll flirt with the idea of pretty poor polymorphism. Inspection of argument value types is a Python programming practice that should be isolated to a few special cases. Later, when we look at numbers and numeric coercion, we'll learn about cases where the inspection of types is recommended.

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. Unit testing is a far better way to find programming errors than verbose type inspection 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 potential use of the isinstance() method is to raise diagnostic errors. A simple approach is to use the assert statement, as follows:

assert isinstance(some_argument, collections.abc.Container), 
f"{some_argument!r} not a Container"

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. This example has two disadvantages: assertions can be silenced, and it would probably be better to raise a TypeError for this. The preceding use of the assert statement is not very helpful, and should be avoided.

The following example is slightly better:

if not isinstance(some_argument, collections.abc.Container): 
    raise TypeError(f"{some_argument!r} not a Container")

The preceding code has the advantage that it raises the correct error. However, it has the disadvantages of being long-winded and it creates a needless constraint on the domain of objects. Objects that are not proper subclasses of the abstract Container class may still offer the required methods, and should not be excluded.

The 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).

Checking types in advance is often called look before you leap (LBYL) programming. It's an overhead of relatively little value. The alternative is called easier to ask for forgiveness than permission (EAFP) programming, and relies on try statements to recover from problems.

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 generally the best approach:

try: 
    found = value in some_argument 
except TypeError: 
    if not isinstance(some_argument, collections.abc.Container): 
        warnings.warn(f"{some_argument!r} not a Container") 
    raise 

The assignment statement to create the found variable 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 of a class that can't use the in operator, the application will write a diagnostic warning message and crash with a TypeError exception.

Many classes work with the in operator. Trying to wrap this in LBYL if statements may exclude a perfectly workable class. Using the EAFP style allows any class to be used that implements the in operator.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset