5.2 Overriding a Built-In Method
Credit: Dave Haynes
5.2.1 Problem
You need to wrap (or, in Python 2.2,
inherit from) a list or tuple, delegating several operations to it,
and want to provide proper slicing (i.e., through the special method
_ _getitem_ _).
5.2.2 Solution
In most cases, overriding special methods of built-in objects when
you inherit from those objects (or wrap them with automatic
delegation, which is not technically an override) poses no special
challenge. When
inheriting in Python 2.2, you can call the
special method of the superclass with the usual unbound-method
syntax. When wrapping, use the syntax that is specific to the
operation, such as self.data[someindex] for
indexing.
Slicing
is harder, because while slicing should go through the same special
method _ _getitem_ _ as indexing (since Python
2.0), lists and tuples still implement an older approach: the more
limited special method _ _getslice_ _ (and
similarly for _ _setitem_ _ versus _
_setslice_ _ and _ _delitem_ _ versus
_ _delslice_ _). So, you must provide a remedy,
normally with a try/except:
class SliceTester:
def _ _init_ _(self):
self.data = ['zero', 'one', 'two', 'three', 'four']
def _ _getitem_ _(self, indexOrSlice):
try:
return self.data[indexOrSlice]
except TypeError:
return self.data[indexOrSlice.start:indexOrSlice.stop]
5.2.3 Discussion
When a user-defined class wraps (or, in Python 2.2, inherits from) a
list or tuple, it often needs to define the _ _set*_
_ and _ _get*_ _ special methods and
delegate part or all of their operation to the wrapped (or inherited)
built-in object to provide the correct access to the data.
The documentation for Python 2.0 and later deprecates the use of
_ _getslice_ _ and _ _setslice_
_. Instead, it suggests providing suitably extended
versions of _ _getitem_ _ and _ _setitem_
_. This is a truly excellent idea because it enables the
use of the extended-form slicing approaches (including step,
ellipsis, and so on) that Numeric Python has made so deservedly
popular among its regular users. Unfortunately, if you try to pass a
slice object to the item-oriented special methods of a list or tuple
object, you get a TypeError; the underlying C API
still insists on receiving integer parameters, not slice objects in
all their glory, whatever the documentation may say.
Fortunately, working around this problem isn't as
dramatic as all that. You just need to trap the
TypeError you get from trying to index an
old-fashioned sequence with a slice, and remedy it suitably.
Here's the typical self-test code that you can
append to the recipe's module and execute when it is
run as a main script:
if _ _name_ _ == "_ _main_ _":
theSlice = SliceTester( )
a = theSlice[2]
b = theSlice[:3]
print a
print b
In the recipe's SliceTester
example class, the remedy is pretty minimal; it's
just an attempt to use start and stop attributes of the noninteger
index (presumably an instance of the slice
built-in type). You may want to do a lot more (implement step,
ellipsis, and so on).
Note that this recipe doesn't cover all of the cases
in which slices can be used. There is a third argument to the slice
operator that defines the step, or stride, of the slicing. For
example, if data is a Numeric Python array (the
only widely used software that supports slicing in all its glory),
data[0:101:10] returns the sequence
data[0], data[10],
data[20]�up to data[100].
Similarly, data[::-1] returns a sequence
containing the contents of data reversed. The
third argument to the slice operator is stored in the
step attribute of slice objects and is set to
None if a step isn't specified
(as in list[start:end]). Given this, it
shouldn't be a surprise that the recipe shown
earlier will not magically add support for steps to objects that
don't support new-style slices.
The point of this recipe is that you must be aware of these
limitations and take precautionary measures. Also,
don't type-test for an index of
type slice. If normal indexing refuses the index,
you are better off catching the TypeError in an
except clause and entering another
try/except in which you try to
use the index as the slice you
now expect it to be. This lets client code pass you objects that are
polymorphic to slice objects.
5.2.4 See Also
The section of the Language Reference on
slicing; the description of the slice built-in
function in the Library Reference.
|