Subclassing built-in types is straightforward. Actually we have been
doing it all along (whenever we subclass <type 'object'>). Some built-in
types (types.FunctionType, for example) are not
subclassable (not yet, at least). However, here we talk about
subclassing <type 'list'>, <type
'tuple'> and other basic data types.
A regular class statement. | |
Define the method to
be overridden. In this case we will convert all items passed through
| |
Upcall to the base if required. | |
Append a float and... | |
watch it automatically become an integer. | |
Otherwise, it behaves like any other list. | |
This doesn't go
through append. We would have to define
| |
We can set attributes
on our instance. This is because it has a |
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 subclass disallowing any user-defined attributes"
__slots__ = []
ml = MyList()
ml.color = 'red' # raises exception!
class MyListWithFewAttrs(list):
"A list subclass allowing specific user-defined attributes"
__slots__ = ['color']
mla = MyListWithFewAttrs()
mla.color = 'red'
mla.weight = 50 # raises exception!
The
| |
Setting any attribute on this raises an exception. | |
| |
Now, if an attribute has space reserved, it can be used. | |
Otherwise, it cannot. This will raise an exception. |
The purpose and recommended use of __slots__
is for optimization. After a type is defined, its slots cannot be
changed. Also, every subclass 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
subclass 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.
For a list, we massage the arguments and hand them over to list.__init__().
| |
For a tuple, we have to override __new__().
| |
A __new__() should always return. It is supposed to return an instance of the type.
|
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'