Commit 192e490e authored by Ken Manheimer's avatar Ken Manheimer

Replace faulty references that had the (previously unnoticed!)

multiply rendered example sections.
parent 8d0ee8fc
...@@ -38,36 +38,6 @@ ...@@ -38,36 +38,6 @@
</PRE> </PRE>
<p> The <a href="ExtensionClass.html">ExtensionClass</a>. release includes mix-in
extension base classes that can be used to add acquisition as a
feature to extension subclasses. These mix-in classes use the
context-wrapping feature of ExtensionClasses to implement
acquisition. Consider the following example::</p>
<p> import ExtensionClass, Acquisition</p>
<p> class C(ExtensionClass.Base):
color='red'</p>
<p> class A(Acquisition.Implicit):</p>
<p> def report(self):
print self.color</p>
<p> a=A()
c=C()
c.a=A()</p>
<p> c.a.report() # prints <code>red</code></p>
<p> d=C()
d.color='green'
d.a=a</p>
<p> d.a.report() # prints <code>green</code></p>
<p> a.report() # raises an attribute error</p>
<p> The class <code>A</code> inherits acquisition behavior from <p> The class <code>A</code> inherits acquisition behavior from
<code>Acquisition.Implicit</code>. The object, <code>a</code>, "has" the color of <code>Acquisition.Implicit</code>. The object, <code>a</code>, "has" the color of
objects <code>c</code> and <code>d</code> when it is accessed through them, but it objects <code>c</code> and <code>d</code> when it is accessed through them, but it
...@@ -92,32 +62,18 @@ ...@@ -92,32 +62,18 @@
</PRE> </PRE>
<p> Aquisition wrappers provide access to the wrapped objects
through the attributes <code>aq_parent</code>, <code>aq_self</code>, <code>aq_base</code>.
In the example above, the expressions::</p>
<p> <code>c.a.aq_parent is c</code></p>
<p> and:</p> <p> and:</p>
<PRE> <PRE>
'c.a.aq_self is a' 'c.a.aq_self is a'
</PRE> </PRE>
<p> and::</p>
<p> <code>c.a.aq_self is a</code></p>
<p> both evaluate to true, but the expression:</p> <p> both evaluate to true, but the expression:</p>
<PRE> <PRE>
'c.a is a' 'c.a is a'
</PRE> </PRE>
<p> both evaluate to true, but the expression::</p>
<p> <code>c.a is a</code></p>
<p> evaluates to false, because the expression <code>c.a</code> evaluates <p> evaluates to false, because the expression <code>c.a</code> evaluates
to an acquisition wrapper around <code>c</code> and <code>a</code>, not <code>a</code> itself.</p> to an acquisition wrapper around <code>c</code> and <code>a</code>, not <code>a</code> itself.</p>
...@@ -152,12 +108,6 @@ ...@@ -152,12 +108,6 @@
</PRE> </PRE>
<p> When explicit acquisition is used, attributes are not
automatically obtained from the environment. Instead, the
method <code>aq_aquire</code> must be used, as in::</p>
<p> print c.a.aq_acquire(<code>color</code>)</p>
<p> To support explicit acquisition, an object should inherit <p> To support explicit acquisition, an object should inherit
from the mix-in class <code>Acquisition.Explicit</code>.</p> from the mix-in class <code>Acquisition.Explicit</code>.</p>
...@@ -183,14 +133,6 @@ ...@@ -183,14 +133,6 @@
</PRE> </PRE>
<p> For example, in::</p>
<p> class C(Acquisition.Explicit):
id=1
secret=2
color=Acquisition.Acquired
__roles__=Acquisition.Acquired</p>
<p> The <em>only</em> attributes that are automatically acquired from <p> The <em>only</em> attributes that are automatically acquired from
containing objects are <code>color</code>, and <code>__roles__</code>. Note also containing objects are <code>color</code>, and <code>__roles__</code>. Note also
that the <code>__roles__</code> attribute is acquired even though it's that the <code>__roles__</code> attribute is acquired even though it's
...@@ -261,35 +203,6 @@ ...@@ -261,35 +203,6 @@
</PRE> </PRE>
<p> For example, in::</p>
<p> from Acquisition import Explicit</p>
<p> class HandyForTesting:
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__</p>
<p> class E(Explicit, HandyForTesting): pass</p>
<p> class Nice(HandyForTesting):
isNice=1
def __str__(self):
return HandyForTesting.__str__(self)+' and I am nice!'
__repr__=__str__</p>
<p> a=E(<code>a</code>)
a.b=E(<code>b</code>)
a.b.c=E(<code>c</code>)
a.p=Nice(<code>spam</code>)
a.b.p=E(<code>p</code>)</p>
<p> def find_nice(self, ancestor, name, object, extra):
return hasattr(object,'isNice') and object.isNice</p>
<p> print a.b.c.aq_acquire(<code>p</code>, find_nice)</p>
<p> The filtered acquisition in the last line skips over the first <p> The filtered acquisition in the last line skips over the first
attribute it finds with the name <code>p</code>, because the attribute attribute it finds with the name <code>p</code>, because the attribute
doesn't satisfy the condition given in the filter. The output of doesn't satisfy the condition given in the filter. The output of
...@@ -299,13 +212,6 @@ ...@@ -299,13 +212,6 @@
</PRE> </PRE>
<p> The filtered acquisition in the last line skips over the first
attribute it finds with the name <code>p</code>, because the attribute
doesn't satisfy the condition given in the filter. The output of
the last line is::</p>
<p> spam(Nice) and I am nice!</p>
<h2>Acquisition and methods</h2> <h2>Acquisition and methods</h2>
...@@ -346,26 +252,6 @@ ...@@ -346,26 +252,6 @@
</PRE> </PRE>
<p> Consider the following example::</p>
<p> from Acquisition import Implicit</p>
<p> class C(Implicit):
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__</p>
<p> a=C("a")
a.b=C("b")
a.b.pref="spam"
a.b.c=C("c")
a.b.c.color="red"
a.b.c.pref="eggs"
a.x=C("x")</p>
<p> o=a.b.c.x</p>
<p> The expression <code>o.color</code> might be expected to return <code>"red"</code>. In <p> The expression <code>o.color</code> might be expected to return <code>"red"</code>. In
earlier versions of ExtensionClass, however, this expression earlier versions of ExtensionClass, however, this expression
failed. Acquired acquiring objects did not acquire from the failed. Acquired acquiring objects did not acquire from the
......
...@@ -330,16 +330,6 @@ ...@@ -330,16 +330,6 @@
</PRE> </PRE>
<p> Attribute lookup is performed by calling the base extension class
<code>getattr</code> operation for the base extension class that includes C
data, or for the first base extension class, if none of the base
extension classes include C data. <code>ExtensionClass.h</code> defines a
macro <code>Py_FindAttrString</code> that can be used to find an object's
attributes that are stored in the object's instance dictionary or
in the object's class or base classes::</p>
<p> v = Py_FindAttrString(self,name);</p>
<p> where <code>name</code> is a C string containing the attribute name.</p> <p> where <code>name</code> is a C string containing the attribute name.</p>
<p> In addition, a macro is provided that replaces <code>Py_FindMethod</code> <p> In addition, a macro is provided that replaces <code>Py_FindMethod</code>
...@@ -405,22 +395,6 @@ ...@@ -405,22 +395,6 @@
</PRE> </PRE>
<p> A problem occurs when trying to overide methods inherited from
Python base classes. Consider the following example::</p>
<p> from ExtensionClass import Base</p>
<p> class Spam:</p>
<p> def __init__(self, name):
self.name=name</p>
<p> class ECSpam(Base, Spam):</p>
<p> def __init__(self, name, favorite_color):
Spam.__init__(self,name)
self.favorite_color=favorite_color</p>
<p> This implementation will fail when an <code>ECSpam</code> object is <p> This implementation will fail when an <code>ECSpam</code> object is
instantiated. The problem is that <code>ECSpam.__init__</code> calls instantiated. The problem is that <code>ECSpam.__init__</code> calls
<code>Spam.__init__</code>, and <code>Spam.__init__</code> can only be called with a <code>Spam.__init__</code>, and <code>Spam.__init__</code> can only be called with a
...@@ -449,25 +423,6 @@ ...@@ -449,25 +423,6 @@
</PRE> </PRE>
<p> To overcome this problem, extension classes provide a class method
<code>inheritedAttribute</code> that can be used to obtain an inherited
attribute that is suitable for calling with an extension class
instance. Using the <code>inheritedAttribute</code> method, the above
example can be rewritten as::</p>
<p> from ExtensionClass import Base</p>
<p> class Spam:</p>
<p> def __init__(self, name):
self.name=name</p>
<p> class ECSpam(Base, Spam):</p>
<p> def __init__(self, name, favorite_color):
ECSpam.inheritedAttribute(<code>__init__</code>)(self,name)
self.favorite_color=favorite_color</p>
<p> This isn't as pretty but does provide the desired result.</p> <p> This isn't as pretty but does provide the desired result.</p>
...@@ -529,29 +484,6 @@ ...@@ -529,29 +484,6 @@
</PRE> </PRE>
<p> Consider the following example::</p>
<p> import ExtensionClass</p>
<p> class CustomMethod(ExtensionClass.Base):</p>
<p> def __call__(self,ob):
print <code>a %s was called</code> % ob.__class__.__name__</p>
<p> class wrapper:</p>
<p> def __init__(self,m,o): self.meth, self.ob=m,o</p>
<p> def __call__(self): self.meth(self.ob)</p>
<p> def __of__(self,o): return self.wrapper(self,o)</p>
<p> class bar(ExtensionClass.Base):
hi=CustomMethod()</p>
<p> x=bar()
hi=x.hi()</p>
<p> Note that <code>ExtensionClass.Base</code> is a base extension class that <p> Note that <code>ExtensionClass.Base</code> is a base extension class that
provides very basic ExtensionClass behavior. </p> provides very basic ExtensionClass behavior. </p>
...@@ -651,23 +583,6 @@ ...@@ -651,23 +583,6 @@
</PRE> </PRE>
<p> For example::</p>
<p> class C(ExtensionClass.Base):</p>
<p> def get_secret(self):
"Get a secret"
....</p>
<p> c=C()</p>
<p> c.f.__roles__=['Trusted People']</p>
<p> print c.f.__roles__ # outputs ['Trusted People']
print c.f__roles__ # outputs ['Trusted People']</p>
<p> print C.f.__roles__ # fails, unbound method</p>
<p> A bound method attribute is set by setting an attribute in it's <p> A bound method attribute is set by setting an attribute in it's
instance with a name consisting of the concatination of the instance with a name consisting of the concatination of the
method's <code>__name__</code> attribute and the attribute name. method's <code>__name__</code> attribute and the attribute name.
......
...@@ -169,166 +169,6 @@ ...@@ -169,166 +169,6 @@
</PRE> </PRE>
<p> Consider an implementation of a MultiMapping extension type,
without use of the extension class mechanism::</p>
<p> #include "Python.h"</p>
<p> #define UNLESS(E) if(!(E))</p>
<p> typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;</p>
<p> staticforward PyTypeObject MMtype;</p>
<p> static PyObject *
MM_push(MMobject *self, PyObject *args){
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}</p>
<p> static PyObject *
MM_pop(MMobject *self, PyObject *args){
long l;
PyObject *r;
static PyObject *emptyList=0;</p>
<p> UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}</p>
<p> static struct PyMethodDef MM_methods[] = {
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};</p>
<p> static PyObject *
newMMobject(PyObject *ignored, PyObject *args){
MMobject *self;</p>
<p> UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self = PyObject_NEW(MMobject, &MMtype)) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
return (PyObject *)self;
err:
Py_DECREF(self);
return NULL;
}</p>
<p> static void
MM_dealloc(MMobject *self){
Py_XDECREF(self->data);
PyMem_DEL(self);
}</p>
<p> static PyObject *
MM_getattr(MMobject *self, char *name){
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}</p>
<p> static int
MM_length(MMobject *self){
long l=0, el, i;
PyObject *e=0;</p>
<p> UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}</p>
<p> static PyObject *
MM_subscript(MMobject *self, PyObject *key){
long i;
PyObject *e;</p>
<p> UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}</p>
<p> static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};</p>
<p> /* -------------------------------------------------------- */</p>
<p> static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;</p>
<p> static PyTypeObject MMtype = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/</p>
<p> /* Space for future expansion <em>/
0L,0L,0L,0L,
MMtype__doc__ /</em> Documentation string */
};</p>
<p> static struct PyMethodDef MultiMapping_methods[] = {
{"MultiMapping", (PyCFunction)newMMobject, 1,
"MultiMapping() -- Create a new empty multi-mapping"},
{NULL, NULL} /* sentinel */
};</p>
<p> void
initMultiMapping(){
PyObject *m;</p>
<p> m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);</p>
<p> if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}</p>
<p> This module defines an extension type, <code>MultiMapping</code>, and exports a <p> This module defines an extension type, <code>MultiMapping</code>, and exports a
module function, <code>MultiMapping</code>, that creates <code>MultiMapping</code> module function, <code>MultiMapping</code>, that creates <code>MultiMapping</code>
Instances. The type provides two methods, <code>push</code>, and <code>pop</code>, for Instances. The type provides two methods, <code>push</code>, and <code>pop</code>, for
...@@ -521,188 +361,6 @@ ...@@ -521,188 +361,6 @@
</PRE> </PRE>
<p> Now consider an extension class implementation of MultiMapping
objects::</p>
<p> #include "Python.h"
#include "ExtensionClass.h"</p>
<p> #define UNLESS(E) if(!(E))</p>
<p> typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;</p>
<p> staticforward PyExtensionClass MMtype;</p>
<p> static PyObject *
MM_push(self, args)
MMobject *self;
PyObject *args;
{
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}</p>
<p> static PyObject *
MM_pop(self, args)
MMobject *self;
PyObject *args;
{
long l;
PyObject *r;
static PyObject *emptyList=0;</p>
<p> UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}</p>
<p> static PyObject *
MM__init__(self, args)
MMobject *self;
PyObject *args;
{
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
Py_INCREF(Py_None);
return Py_None;
err:
Py_DECREF(self);
return NULL;
}</p>
<p> static struct PyMethodDef MM_methods[] = {
{"__init__", (PyCFunction)MM__init__, 1,
"__init__() -- Create a new empty multi-mapping"},
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};</p>
<p> static void
MM_dealloc(self)
MMobject *self;
{
Py_XDECREF(self->data);
PyMem_DEL(self);
}</p>
<p> static PyObject *
MM_getattr(self, name)
MMobject *self;
char *name;
{
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}</p>
<p> static int
MM_length(self)
MMobject *self;
{
long l=0, el, i;
PyObject *e=0;</p>
<p> UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}</p>
<p> static PyObject *
MM_subscript(self, key)
MMobject *self;
PyObject *key;
{
long i;
PyObject *e;</p>
<p> UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}</p>
<p> static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};</p>
<p> /* -------------------------------------------------------- */</p>
<p> static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;</p>
<p> static PyExtensionClass MMtype = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/</p>
<p> /* Space for future expansion <em>/
0L,0L,0L,0L,
MMtype__doc__, /</em> Documentation string */
METHOD_CHAIN(MM_methods)
};</p>
<p> static struct PyMethodDef MultiMapping_methods[] = {
{NULL, NULL} /* sentinel */
};</p>
<p> void
initMultiMapping()
{
PyObject *m, *d;</p>
<p> m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);</p>
<p> if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}</p>
<p> This version includes <code>ExtensionClass.h</code>. The two declarations of <p> This version includes <code>ExtensionClass.h</code>. The two declarations of
<code>MMtype</code> have been changed from <code>PyTypeObject</code> to <code>PyExtensionClass</code>. <code>MMtype</code> have been changed from <code>PyTypeObject</code> to <code>PyExtensionClass</code>.
The <code>METHOD_CHAIN</code> macro has been used to add methods to the end of The <code>METHOD_CHAIN</code> macro has been used to add methods to the end of
...@@ -715,16 +373,6 @@ ...@@ -715,16 +373,6 @@
</PRE> </PRE>
<p> This version includes <code>ExtensionClass.h</code>. The two declarations of
<code>MMtype</code> have been changed from <code>PyTypeObject</code> to <code>PyExtensionClass</code>.
The <code>METHOD_CHAIN</code> macro has been used to add methods to the end of
the definition for <code>MMtype</code>. The module function, newMMobject has
been replaced by the <code>MMtype</code> method, <code>MM__init__</code>. Note that this
method does not create or return a new object. Finally, the lines::</p>
<p> d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);</p>
<p> Have been added to both initialize the extension class and to export <p> Have been added to both initialize the extension class and to export
it in the module dictionary.</p> it in the module dictionary.</p>
...@@ -743,19 +391,6 @@ ...@@ -743,19 +391,6 @@
</PRE> </PRE>
<p> To use this module, compile, link, and import it as with any other
extension module. The following python code illustrates the
module's use::</p>
<p> from MultiMapping import MultiMapping
m=MultiMapping()
m.push({'spam':1, <code>eggs</code>:2})
m.push({'spam':3, <code>ham</code>:4})</p>
<p> m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error</p>
<p> Creating the <code>MultiMapping</code> object took three steps, one to create <p> Creating the <code>MultiMapping</code> object took three steps, one to create
an empty <code>MultiMapping</code>, and two to add mapping objects to it. We an empty <code>MultiMapping</code>, and two to add mapping objects to it. We
might wish to simplify the process of creating MultiMapping might wish to simplify the process of creating MultiMapping
...@@ -777,25 +412,6 @@ ...@@ -777,25 +412,6 @@
</PRE> </PRE>
<p> Creating the <code>MultiMapping</code> object took three steps, one to create
an empty <code>MultiMapping</code>, and two to add mapping objects to it. We
might wish to simplify the process of creating MultiMapping
objects by providing a constructor that takes source mapping
objects as parameters. We can do this by sub-classing MultiMapping
in Python::</p>
<p> from MultiMapping import MultiMapping
class ExtendedMultiMapping(MultiMapping):
def __init__(self,*data):
MultiMapping.__init__(self)
for d in data: self.push(d)</p>
<p> m=ExtendedMultiMapping({'spam':1, <code>eggs</code>:2}, {'spam':3, <code>ham</code>:4})</p>
<p> m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error</p>
<p> Note that the source file included in the ExtensionClass <p> Note that the source file included in the ExtensionClass
distribution has numerous enhancements beyond the version shown in distribution has numerous enhancements beyond the version shown in
this document. this document.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment