Fill out section on how to write a new-style class

This commit is contained in:
Andrew M. Kuchling 2001-10-23 20:26:16 +00:00
parent f66dacdb01
commit 4855b02554
1 changed files with 145 additions and 7 deletions

View File

@ -1,3 +1,4 @@
\documentclass{howto} \documentclass{howto}
% $Id$ % $Id$
@ -129,8 +130,95 @@ section apply only to new-style classes. This divergence isn't
intended to last forever; eventually old-style classes will be intended to last forever; eventually old-style classes will be
dropped, possibly in Python 3.0. dropped, possibly in Python 3.0.
So how do you define a new-style class? XXX So how do you define a new-style class? You do it by subclassing an
Subclass object -- subclass a built-in type. existing new-style class. Most of Python's built-in types, such as
integers, lists, dictionaries, and even files, are new-style classes
now. A new-style class named \class{object}, the base class for all
built-in types, has been also been added so if no built-in type is
suitable, you can just subclass \class{object}:
\begin{verbatim}
class C(object):
def __init__ (self):
...
...
\end{verbatim}
This means that \keyword{class} statements that don't have any base
classes are always classic classes in Python 2.2. There's actually a
way to make new-style classes without any base classes, by setting the
\member{__metaclass__} variable to XXX. (What do you set it to?)
The type objects for the built-in types are available as built-ins,
named using a clever trick. Python has always had built-in functions
named \function{int()}, \function{float()}, and \function{str()}. In
2.2, they aren't functions any more, but type objects that behave as
factories when called.
\begin{verbatim}
>>> int
<type 'int'>
>>> int('123')
123
\end{verbatim}
To make the set of types complete, new type objects such as
\function{dictionary} and \function{file} have been added.
Here's a more interesting example. The following class subclasses
Python's dictionary implementation in order to automatically fold all
dictionary keys to lowercase.
\begin{verbatim}
class LowerCaseDict(dictionary):
def _fold_key (self, key):
if not isinstance(key, str):
raise TypeError, "All keys must be strings"
return key.lower()
def __getitem__ (self, key):
key = self._fold_key(key)
return dictionary.__getitem__(self, key)
def __setitem__ (self, key, value):
key = self._fold_key(key)
dictionary.__setitem__(self, key, value)
def __delitem__ (self, key):
key = self._fold_key(key)
dictionary.__delitem__(self, key, value)
\end{verbatim}
Trying out this class, it works as you'd expect:
\begin{verbatim}
>>> d = LowerCaseDict()
>>> d['ABC'] = 1
>>> d['abc']
1
\end{verbatim}
However, because it's a subclass of Python's dictionary type,
instances of \class{LowerCaseDict} can be used in most places where a
regular dictionary is required.
\begin{verbatim}
>>> d = LowerCaseDict()
>>> exec 'Name = 1' in d
>>> print d.items()
XXX
>>> exec 'nAmE = name + 1' in d
>>> print d.items()
XXX
\end{verbatim}
And now you can have Python with case-insensitive variable names! One
of the nice things about Python 2.2 is that it makes Python flexible
enough to solve many other past problems without hacking Python's C
code. If you want a case-insensitive Python environment, using a
case-folding dictionary and writing a case-insensitive tokenizer using
the compiler package (now automatically installed in 2.2) will make it
a straightforward.
\subsection{Descriptors} \subsection{Descriptors}
@ -233,14 +321,66 @@ write \function{eiffelmethod()} or the ZODB or whatever, but most
users will just write code on top of the resulting libraries and users will just write code on top of the resulting libraries and
ignore the implementation details. ignore the implementation details.
\subsection{Inheritance Lookup: The Diamond Rule} \subsection{Multiple Inheritance: The Diamond Rule}
Multiple inheritance has also been made more useful through changing
the rules under which names are resolved. Consider this set of classes
(diagram taken from \pep{253} by Guido van Rossum):
\begin{verbatim}
class A:
^ ^ def save(self): ...
/ \
/ \
/ \
/ \
class B class C:
^ ^ def save(self): ...
\ /
\ /
\ /
\ /
class D
\end{verbatim}
The lookup rule for classic classes is simple but not very smart; the
base classes are searched depth-first, going from left to right. A
reference to \method{D.save} will search the classes \class{D},
\class{B}, and then \class{A}, where \method{save()} would be found
and returned. \method{C.save()} would never be found at all. This is
bad, because if \class{C}'s \method{save()} method is saving some
internal state specific to \class{C}, not calling it will result in
that state never getting saved.
New-style classes follow a different algorithm that's a bit more
complicated to explain, but does the right thing in this situation.
\begin{enumerate}
\item List all the base classes, following the classic lookup rule and
include a class multiple times if it's visited repeatedly. In the
above example, the list of visited classes is [\class{D}, \class{B},
\class{A}, \class{C}, class{A}].
\item Scan the list for duplicated classes. If any are found, remove
all but one occurrence, leaving the \emph{last} one in the list. In
the above example, the list becomes [\class{D}, \class{B}, \class{C},
class{A}] after dropping duplicates.
\end{enumerate}
Following this rule, referring to \method{D.save()} will return
\method{C.save()}, which is the behaviour we're after. This lookup
rule is the same as the one followed by XXX Common Lisp?.
XXX
\subsection{Attribute Access} \subsection{Attribute Access}
XXX __getattribute__, __getattr__ XXX __getattribute__, __getattr__
XXX properties, slots
\subsection{Related Links} \subsection{Related Links}
\ref{sect-rellinks} \ref{sect-rellinks}
@ -264,6 +404,7 @@ Guido van Rossum, with substantial assistance from the rest of the
Zope Corp. team. Zope Corp. team.
Finally, there's the ultimate authority: the source code. Finally, there's the ultimate authority: the source code.
typeobject.c, others?
% XXX point people at the right files % XXX point people at the right files
@ -349,7 +490,6 @@ means you can do things like this:
>>> a,b,c = i >>> a,b,c = i
>>> a,b,c >>> a,b,c
(1, 2, 3) (1, 2, 3)
>>>
\end{verbatim} \end{verbatim}
Iterator support has been added to some of Python's basic types. Iterator support has been added to some of Python's basic types.
@ -373,7 +513,6 @@ Apr 4
Nov 11 Nov 11
Dec 12 Dec 12
Oct 10 Oct 10
>>>
\end{verbatim} \end{verbatim}
That's just the default behaviour. If you want to iterate over keys, That's just the default behaviour. If you want to iterate over keys,
@ -471,7 +610,6 @@ Traceback (most recent call last):
File "<stdin>", line 1, in ? File "<stdin>", line 1, in ?
File "<stdin>", line 2, in generate_ints File "<stdin>", line 2, in generate_ints
StopIteration StopIteration
>>>
\end{verbatim} \end{verbatim}
You could equally write \code{for i in generate_ints(5)}, or You could equally write \code{for i in generate_ints(5)}, or