Subtyping built-in types is straightforward. Actually we have been
doing it all along (whenever we subtype <type 'object'>). Some built-in
types (types.FunctionType, for example) are not
subclassable (not yet, at least). However, here we talk about
subtyping <type 'list'>, <type
'tuple'> and other basic data types.
Example 3.3. Subtyping <type 'list'>
>>> class MyList(list):... "A list that converts appended items to ints" ... def append(self, item):
... list.append(self, int(item))
... >>> >>> l = MyList() >>> l.append(1.3)
>>> l.append(444) >>> l [1, 444]
>>> len(l)
2 >>> l[1] = 1.2
>>> l [1, 1.2] >>> l.color = 'red'
![]()
Basic lists do not have __dict__ (and so no
user-defined attributes), but ours does. This is usually not a problem
and may even be what we want. If we use a very
large number of MyLists, however, we could optimize
our program by telling Python not to create the
__dict__ for instances of
MyList.
Example 3.4. Using __slots__ for optimization
class MyList(list):
"A list subtype disallowing any user-defined attributes"
__slots__ = []
ml = MyList()
ml.color = 'red' # raises exception!
class MyListWithFewAttrs(list):
"A list subtype allowing specific user-defined attributes"
__slots__ = ['color']
mla = MyListWithFewAttrs()
mla.color = 'red'
mla.weight = 50 # raises exception!
The purpose and recommended use of __slots__
is for optimization. After a type is defined, its slots cannot be
changed. Also, every subtype must define __slots__,
otherwise its instances will end up having __dict__.
We can create a list even by instantiating it like any other type:
list([1,2,3]). This means
list.__init__() accepts the same argument (i.e. any
iterable) and initializes a list. We can customize initialization in a
subtype by redefining __init__() and
upcalling __init__() on the
base.
Tuples are immutable and different from lists. Once an instance
is created, it cannot be changed. Note that the instance of a type
already exists when __init__() is called (in fact
the instance is passed as the first argument). The
__new__() static method of a type is called to
create an instance of the type. It is passed the
type itself as the first argument, and passed through other initial
arguments (similar to __init__()). We use this to
customize immutable types like a tuple.
Example 3.5. Customizing creation of subtypes
class MyList(list):
def __init__(self, itr):
list.__init__(self, [int(x) for x in itr])
class MyTuple(tuple):
def __new__(typ, itr):
seq = [int(x) for x in itr]
return tuple.__new__(typ, seq)
The __new__() method is not special to immutable
types, it is used for all types. It is also converted to a static
method automatically by Python (by virtue of its name).
2
>>> l[1] = 1.2
>>> l
[1, 1.2]
>>> l.color = 'red'