## With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

No credit card required

# Checking if an Object Has Necessary Attributes

Credit: Alex Martelli

## Problem

You need to check if an object has certain necessary attributes, before performing state-altering operations, but you want to avoid type-testing because you know it reduces polymorphism.

## Solution

In Python, you normally try whatever operations you need to perform. For example, here’s the simplest, no-checks code for manipulations of a list:

```def munge1(alist):
alist.append(23)
alist.extend(range(5))
alist.append(42)
alist[4] = alist[3]
alist.extend(range(2))```

While this is usually adequate, there may be occasional problems. For example, if the `alist` object has an `append` method but not an `extend` method, the `munge1` function will partially alter `alist` before an exception is raised. Such partial alterations are generally not cleanly undoable, and, depending on your application, they can be quite a bother.

To avoid partial alteration, you might want to check the type. A naive Look Before You Leap (LBYL) approach looks safer, but it has a serious defect: it loses polymorphism. The worst approach of all is checking for equality of types:

```def munge2(alist):
if type(alist)==type([]):
munge1(alist)
else: raise TypeError, "expected list, got %s"%type(alist)```

A better, but still unfavorable, approach (which at least works for list subclasses in 2.2) is using `isinstance`:

```def munge3(alist):
if isinstance(alist, type[]):
munge1(alist)
else: raise TypeError, "expected list, got %s"%type(alist)```

The proper solution is accurate LBYL, which is safer and fully polymorphic:

```def munge4(alist):
# Extract all bound methods you need (immediate exception
# if any needed method is missing)
append = alist.append
extend = alist.extend

# Check operations, such as indexing, to raise
# exceptions ASAP if signature compatibility is missing
try: a[0]=a[0]
except IndexError: pass    # An empty alist is okay

# Operate -- no exceptions expected at this point
append(23)
extend(range(5))
append(42)
alist[4] = alist[3]
extend(range(2))```

## Discussion

Python functions are naturally polymorphic on their arguments, and checking argument types loses polymorphism. However, we may still get early checks and some extra safety without any substantial cost.

The Easier to Ask Forgiveness than Permission (EAFP) approach, in which we try operations and handle any resulting exceptions, is the normal Pythonic way of life and usually works great. Explicit checking of types severely restricts Python’s normal signature-based polymorphism and should be avoided in most cases. However, if we need to perform several operations on an object, trying to do them all could result in some of them succeeding and partially altering the object before an exception is raised.

For example, suppose that `munge1`, in the recipe’s code, is called with an actual argument value for `alist` that has an `append` method but lacks `extend`. In this case, `alist` will be altered by the first call to `append`, and the attempt to call `extend` will raise an exception, leaving `alist`’s state partially altered in a way that may be hard to recover from. Sometimes, a sequence of operations should be atomic: either all of the alterations happen or none of them do.

We can get closer to that by switching to LBYL, but in an accurate, careful way. Typically, we extract all bound methods we’ll need, then noninvasively test the necessary operations (such as indexing on both sides of the assignment operator). We move on to actually changing the object state only if all of this succeeds. From there, it’s far less likely (though not impossible) that exceptions will occur in midstream, with state partially altered.

This extra complication is pretty modest, and the slowdown due to the checks is typically more or less compensated by the extra speed of using bound methods versus explicit attribute access (at least if the operations include loops, which is often the case). It’s important to avoid overdoing the checks, and `assert` can help with that. For example, you can add `assert callable(append)` to `munge4( )`. In this case, the compiler will remove the `assert` entirely when the program is run with optimization (i.e., with flags `-O` or `-OO`), while performing the checks when the program is run for testing and debugging (i.e., without the optimization flags).

`assert` and the meaning of the `-O` and `-OO` command-line arguments are defined in all Python reference texts; the Library Reference section on sequence types.