Commit ecccdc29 authored by Guido van Rossum's avatar Guido van Rossum

- Patch 1433928:

  - The copy module now "copies" function objects (as atomic objects).
  - dict.__getitem__ now looks for a __missing__ hook before raising
    KeyError.
  - Added a new type, defaultdict, to the collections module.
    This uses the new __missing__ hook behavior added to dict (see above).
parent 11d2c8fe
...@@ -8,9 +8,10 @@ ...@@ -8,9 +8,10 @@
\versionadded{2.4} \versionadded{2.4}
This module implements high-performance container datatypes. Currently, the This module implements high-performance container datatypes. Currently,
only datatype is a deque. Future additions may include B-trees there are two datatypes, deque and defaultdict.
and Fibonacci heaps. Future additions may include B-trees and Fibonacci heaps.
\versionchanged[Added defaultdict]{2.5}
\begin{funcdesc}{deque}{\optional{iterable}} \begin{funcdesc}{deque}{\optional{iterable}}
Returns a new deque objected initialized left-to-right (using Returns a new deque objected initialized left-to-right (using
...@@ -211,3 +212,46 @@ def maketree(iterable): ...@@ -211,3 +212,46 @@ def maketree(iterable):
[[[['a', 'b'], ['c', 'd']], [['e', 'f'], ['g', 'h']]]] [[[['a', 'b'], ['c', 'd']], [['e', 'f'], ['g', 'h']]]]
\end{verbatim} \end{verbatim}
\begin{funcdesc}{defaultdict}{\optional{default_factory\optional{, ...}}}
Returns a new dictionary-like object. \class{defaultdict} is a subclass
of the builtin \class{dict} class. It overrides one method and adds one
writable instance variable. The remaining functionality is the same as
for the \class{dict} class and is not documented here.
The first argument provides the initial value for the
\member{default_factory} attribute; it defaults to \code{None}.
All remaining arguments are treated the same as if they were
passed to the \class{dict} constructor, including keyword arguments.
\versionadded{2.5}
\end{funcdesc}
\class{defaultdict} objects support the following method in addition to
the standard \class{dict} operations:
\begin{methoddesc}{__missing__}{key}
If the \member{default_factory} attribute is \code{None}, this raises
an \exception{KeyError} exception with the \var{key} as argument.
If \member{default_factory} is not \code{None}, it is called without
arguments to provide a default value for the given \var{key}, this
value is inserted in the dictionary for the \var{key}, and returned.
If calling \member{default_factory} raises an exception this exception
is propagated unchanged.
This method is called by the \method{__getitem__} method of the
\class{dict} class when the requested key is not found; whatever it
returns or raises is then returned or raised by \method{__getitem__}.
\end{methoddesc}
\class{defaultdict} objects support the following instance variable:
\begin{datadesc}{default_factory}
This attribute is used by the \method{__missing__} method; it is initialized
from the first argument to the constructor, if present, or to \code{None},
if absent.
\end{datadesc}
...@@ -67,9 +67,12 @@ set of components copied. ...@@ -67,9 +67,12 @@ set of components copied.
\end{itemize} \end{itemize}
This version does not copy types like module, class, function, method, This module does not copy types like module, method,
stack trace, stack frame, file, socket, window, array, or any similar stack trace, stack frame, file, socket, window, array, or any similar
types. types. It does ``copy'' functions and classes (shallow and deeply),
by returning the original object unchanged; this is compatible with
the way these are treated by the \module{pickle} module.
\versionchanged[Added copying functions]{2.5}
Classes can use the same interfaces to control copying that they use Classes can use the same interfaces to control copying that they use
to control pickling. See the description of module to control pickling. See the description of module
......
...@@ -1350,7 +1350,7 @@ arbitrary objects): ...@@ -1350,7 +1350,7 @@ arbitrary objects):
\begin{tableiii}{c|l|c}{code}{Operation}{Result}{Notes} \begin{tableiii}{c|l|c}{code}{Operation}{Result}{Notes}
\lineiii{len(\var{a})}{the number of items in \var{a}}{} \lineiii{len(\var{a})}{the number of items in \var{a}}{}
\lineiii{\var{a}[\var{k}]}{the item of \var{a} with key \var{k}}{(1)} \lineiii{\var{a}[\var{k}]}{the item of \var{a} with key \var{k}}{(1), (10)}
\lineiii{\var{a}[\var{k}] = \var{v}} \lineiii{\var{a}[\var{k}] = \var{v}}
{set \code{\var{a}[\var{k}]} to \var{v}} {set \code{\var{a}[\var{k}]} to \var{v}}
{} {}
...@@ -1454,6 +1454,17 @@ then is updated with those key/value pairs: ...@@ -1454,6 +1454,17 @@ then is updated with those key/value pairs:
\versionchanged[Allowed the argument to be an iterable of key/value \versionchanged[Allowed the argument to be an iterable of key/value
pairs and allowed keyword arguments]{2.4} pairs and allowed keyword arguments]{2.4}
\item[(10)] If a subclass of dict defines a method \method{__missing__},
if the key \var{k} is not present, the \var{a}[\var{k}] operation calls
that method with the key \var{k} as argument. The \var{a}[\var{k}]
operation then returns or raises whatever is returned or raised by the
\function{__missing__}(\var{k}) call if the key is not present.
No other operations or methods invoke \method{__missing__}().
If \method{__missing__} is not defined, \exception{KeyError} is raised.
\method{__missing__} must be a method; it cannot be an instance variable.
For an example, see \module{collections}.\class{defaultdict}.
\versionadded{2.5}
\end{description} \end{description}
\subsection{File Objects \subsection{File Objects
......
...@@ -14,7 +14,12 @@ class UserDict: ...@@ -14,7 +14,12 @@ class UserDict:
else: else:
return cmp(self.data, dict) return cmp(self.data, dict)
def __len__(self): return len(self.data) def __len__(self): return len(self.data)
def __getitem__(self, key): return self.data[key] def __getitem__(self, key):
if key in self.data:
return self.data[key]
if hasattr(self.__class__, "__missing__"):
return self.__class__.__missing__(self, key)
raise KeyError(key)
def __setitem__(self, key, item): self.data[key] = item def __setitem__(self, key, item): self.data[key] = item
def __delitem__(self, key): del self.data[key] def __delitem__(self, key): del self.data[key]
def clear(self): self.data.clear() def clear(self): self.data.clear()
......
...@@ -101,7 +101,8 @@ def _copy_immutable(x): ...@@ -101,7 +101,8 @@ def _copy_immutable(x):
return x return x
for t in (type(None), int, long, float, bool, str, tuple, for t in (type(None), int, long, float, bool, str, tuple,
frozenset, type, xrange, types.ClassType, frozenset, type, xrange, types.ClassType,
types.BuiltinFunctionType): types.BuiltinFunctionType,
types.FunctionType):
d[t] = _copy_immutable d[t] = _copy_immutable
for name in ("ComplexType", "UnicodeType", "CodeType"): for name in ("ComplexType", "UnicodeType", "CodeType"):
t = getattr(types, name, None) t = getattr(types, name, None)
...@@ -217,6 +218,7 @@ d[type] = _deepcopy_atomic ...@@ -217,6 +218,7 @@ d[type] = _deepcopy_atomic
d[xrange] = _deepcopy_atomic d[xrange] = _deepcopy_atomic
d[types.ClassType] = _deepcopy_atomic d[types.ClassType] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
def _deepcopy_list(x, memo): def _deepcopy_list(x, memo):
y = [] y = []
......
...@@ -568,6 +568,22 @@ class TestCopy(unittest.TestCase): ...@@ -568,6 +568,22 @@ class TestCopy(unittest.TestCase):
raise ValueError, "ain't got no stickin' state" raise ValueError, "ain't got no stickin' state"
self.assertRaises(ValueError, copy.copy, EvilState()) self.assertRaises(ValueError, copy.copy, EvilState())
def test_copy_function(self):
self.assertEqual(copy.copy(global_foo), global_foo)
def foo(x, y): return x+y
self.assertEqual(copy.copy(foo), foo)
bar = lambda: None
self.assertEqual(copy.copy(bar), bar)
def test_deepcopy_function(self):
self.assertEqual(copy.deepcopy(global_foo), global_foo)
def foo(x, y): return x+y
self.assertEqual(copy.deepcopy(foo), foo)
bar = lambda: None
self.assertEqual(copy.deepcopy(bar), bar)
def global_foo(x, y): return x+y
def test_main(): def test_main():
test_support.run_unittest(TestCopy) test_support.run_unittest(TestCopy)
......
"""Unit tests for collections.defaultdict."""
import os
import copy
import tempfile
import unittest
from collections import defaultdict
def foobar():
return list
class TestDefaultDict(unittest.TestCase):
def test_basic(self):
d1 = defaultdict()
self.assertEqual(d1.default_factory, None)
d1.default_factory = list
d1[12].append(42)
self.assertEqual(d1, {12: [42]})
d1[12].append(24)
self.assertEqual(d1, {12: [42, 24]})
d1[13]
d1[14]
self.assertEqual(d1, {12: [42, 24], 13: [], 14: []})
self.assert_(d1[12] is not d1[13] is not d1[14])
d2 = defaultdict(list, foo=1, bar=2)
self.assertEqual(d2.default_factory, list)
self.assertEqual(d2, {"foo": 1, "bar": 2})
self.assertEqual(d2["foo"], 1)
self.assertEqual(d2["bar"], 2)
self.assertEqual(d2[42], [])
self.assert_("foo" in d2)
self.assert_("foo" in d2.keys())
self.assert_("bar" in d2)
self.assert_("bar" in d2.keys())
self.assert_(42 in d2)
self.assert_(42 in d2.keys())
self.assert_(12 not in d2)
self.assert_(12 not in d2.keys())
d2.default_factory = None
self.assertEqual(d2.default_factory, None)
try:
d2[15]
except KeyError, err:
self.assertEqual(err.args, (15,))
else:
self.fail("d2[15] didn't raise KeyError")
def test_missing(self):
d1 = defaultdict()
self.assertRaises(KeyError, d1.__missing__, 42)
d1.default_factory = list
self.assertEqual(d1.__missing__(42), [])
def test_repr(self):
d1 = defaultdict()
self.assertEqual(d1.default_factory, None)
self.assertEqual(repr(d1), "defaultdict(None, {})")
d1[11] = 41
self.assertEqual(repr(d1), "defaultdict(None, {11: 41})")
d2 = defaultdict(0)
self.assertEqual(d2.default_factory, 0)
d2[12] = 42
self.assertEqual(repr(d2), "defaultdict(0, {12: 42})")
def foo(): return 43
d3 = defaultdict(foo)
self.assert_(d3.default_factory is foo)
d3[13]
self.assertEqual(repr(d3), "defaultdict(%s, {13: 43})" % repr(foo))
def test_print(self):
d1 = defaultdict()
def foo(): return 42
d2 = defaultdict(foo, {1: 2})
# NOTE: We can't use tempfile.[Named]TemporaryFile since this
# code must exercise the tp_print C code, which only gets
# invoked for *real* files.
tfn = tempfile.mktemp()
try:
f = open(tfn, "w+")
try:
print >>f, d1
print >>f, d2
f.seek(0)
self.assertEqual(f.readline(), repr(d1) + "\n")
self.assertEqual(f.readline(), repr(d2) + "\n")
finally:
f.close()
finally:
os.remove(tfn)
def test_copy(self):
d1 = defaultdict()
d2 = d1.copy()
self.assertEqual(type(d2), defaultdict)
self.assertEqual(d2.default_factory, None)
self.assertEqual(d2, {})
d1.default_factory = list
d3 = d1.copy()
self.assertEqual(type(d3), defaultdict)
self.assertEqual(d3.default_factory, list)
self.assertEqual(d3, {})
d1[42]
d4 = d1.copy()
self.assertEqual(type(d4), defaultdict)
self.assertEqual(d4.default_factory, list)
self.assertEqual(d4, {42: []})
d4[12]
self.assertEqual(d4, {42: [], 12: []})
def test_shallow_copy(self):
d1 = defaultdict(foobar, {1: 1})
d2 = copy.copy(d1)
self.assertEqual(d2.default_factory, foobar)
self.assertEqual(d2, d1)
d1.default_factory = list
d2 = copy.copy(d1)
self.assertEqual(d2.default_factory, list)
self.assertEqual(d2, d1)
def test_deep_copy(self):
d1 = defaultdict(foobar, {1: [1]})
d2 = copy.deepcopy(d1)
self.assertEqual(d2.default_factory, foobar)
self.assertEqual(d2, d1)
self.assert_(d1[1] is not d2[1])
d1.default_factory = list
d2 = copy.deepcopy(d1)
self.assertEqual(d2.default_factory, list)
self.assertEqual(d2, d1)
if __name__ == "__main__":
unittest.main()
...@@ -395,6 +395,56 @@ class DictTest(unittest.TestCase): ...@@ -395,6 +395,56 @@ class DictTest(unittest.TestCase):
else: else:
self.fail("< didn't raise Exc") self.fail("< didn't raise Exc")
def test_missing(self):
# Make sure dict doesn't have a __missing__ method
self.assertEqual(hasattr(dict, "__missing__"), False)
self.assertEqual(hasattr({}, "__missing__"), False)
# Test several cases:
# (D) subclass defines __missing__ method returning a value
# (E) subclass defines __missing__ method raising RuntimeError
# (F) subclass sets __missing__ instance variable (no effect)
# (G) subclass doesn't define __missing__ at a all
class D(dict):
def __missing__(self, key):
return 42
d = D({1: 2, 3: 4})
self.assertEqual(d[1], 2)
self.assertEqual(d[3], 4)
self.assert_(2 not in d)
self.assert_(2 not in d.keys())
self.assertEqual(d[2], 42)
class E(dict):
def __missing__(self, key):
raise RuntimeError(key)
e = E()
try:
e[42]
except RuntimeError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("e[42] didn't raise RuntimeError")
class F(dict):
def __init__(self):
# An instance variable __missing__ should have no effect
self.__missing__ = lambda key: None
f = F()
try:
f[42]
except KeyError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("f[42] didn't raise KeyError")
class G(dict):
pass
g = G()
try:
g[42]
except KeyError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("g[42] didn't raise KeyError")
import mapping_tests import mapping_tests
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
......
...@@ -148,6 +148,55 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): ...@@ -148,6 +148,55 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol):
self.assertEqual(t.popitem(), ("x", 42)) self.assertEqual(t.popitem(), ("x", 42))
self.assertRaises(KeyError, t.popitem) self.assertRaises(KeyError, t.popitem)
def test_missing(self):
# Make sure UserDict doesn't have a __missing__ method
self.assertEqual(hasattr(UserDict, "__missing__"), False)
# Test several cases:
# (D) subclass defines __missing__ method returning a value
# (E) subclass defines __missing__ method raising RuntimeError
# (F) subclass sets __missing__ instance variable (no effect)
# (G) subclass doesn't define __missing__ at a all
class D(UserDict.UserDict):
def __missing__(self, key):
return 42
d = D({1: 2, 3: 4})
self.assertEqual(d[1], 2)
self.assertEqual(d[3], 4)
self.assert_(2 not in d)
self.assert_(2 not in d.keys())
self.assertEqual(d[2], 42)
class E(UserDict.UserDict):
def __missing__(self, key):
raise RuntimeError(key)
e = E()
try:
e[42]
except RuntimeError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("e[42] didn't raise RuntimeError")
class F(UserDict.UserDict):
def __init__(self):
# An instance variable __missing__ should have no effect
self.__missing__ = lambda key: None
UserDict.UserDict.__init__(self)
f = F()
try:
f[42]
except KeyError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("f[42] didn't raise KeyError")
class G(UserDict.UserDict):
pass
g = G()
try:
g[42]
except KeyError, err:
self.assertEqual(err.args, (42,))
else:
self.fail_("g[42] didn't raise KeyError")
########################## ##########################
# Test Dict Mixin # Test Dict Mixin
......
...@@ -12,6 +12,11 @@ What's New in Python 2.5 alpha 1? ...@@ -12,6 +12,11 @@ What's New in Python 2.5 alpha 1?
Core and builtins Core and builtins
----------------- -----------------
- Patch 1433928:
- The copy module now "copies" function objects (as atomic objects).
- dict.__getitem__ now looks for a __missing__ hook before raising
KeyError.
- Fix the encodings package codec search function to only search - Fix the encodings package codec search function to only search
inside its own package. Fixes problem reported in patch #1433198. inside its own package. Fixes problem reported in patch #1433198.
...@@ -224,6 +229,9 @@ Core and builtins ...@@ -224,6 +229,9 @@ Core and builtins
Extension Modules Extension Modules
----------------- -----------------
- Patch 1433928: Added a new type, defaultdict, to the collections module.
This uses the new __missing__ hook behavior added to dict (see above).
- Bug #854823: socketmodule now builds on Sun platforms even when - Bug #854823: socketmodule now builds on Sun platforms even when
INET_ADDRSTRLEN is not defined. INET_ADDRSTRLEN is not defined.
......
...@@ -1065,10 +1065,269 @@ PyTypeObject dequereviter_type = { ...@@ -1065,10 +1065,269 @@ PyTypeObject dequereviter_type = {
0, 0,
}; };
/* defaultdict type *********************************************************/
typedef struct {
PyDictObject dict;
PyObject *default_factory;
} defdictobject;
static PyTypeObject defdict_type; /* Forward */
PyDoc_STRVAR(defdict_missing_doc,
"__missing__(key) # Called by __getitem__ for missing key; pseudo-code:\n\
if self.default_factory is None: raise KeyError(key)\n\
self[key] = value = self.default_factory()\n\
return value\n\
");
static PyObject *
defdict_missing(defdictobject *dd, PyObject *key)
{
PyObject *factory = dd->default_factory;
PyObject *value;
if (factory == NULL || factory == Py_None) {
/* XXX Call dict.__missing__(key) */
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
value = PyEval_CallObject(factory, NULL);
if (value == NULL)
return value;
if (PyObject_SetItem((PyObject *)dd, key, value) < 0) {
Py_DECREF(value);
return NULL;
}
return value;
}
PyDoc_STRVAR(defdict_copy_doc, "D.copy() -> a shallow copy of D.");
static PyObject *
defdict_copy(defdictobject *dd)
{
/* This calls the object's class. That only works for subclasses
whose class constructor has the same signature. Subclasses that
define a different constructor signature must override copy().
*/
return PyObject_CallFunctionObjArgs((PyObject *)dd->dict.ob_type,
dd->default_factory, dd, NULL);
}
static PyObject *
defdict_reduce(defdictobject *dd)
{
/* __reduce__ must returns a 5-tuple as follows:
- factory function
- tuple of args for the factory function
- additional state (here None)
- sequence iterator (here None)
- dictionary iterator (yielding successive (key, value) pairs
This API is used by pickle.py and copy.py.
For this to be useful with pickle.py, the default_factory
must be picklable; e.g., None, a built-in, or a global
function in a module or package.
Both shallow and deep copying are supported, but for deep
copying, the default_factory must be deep-copyable; e.g. None,
or a built-in (functions are not copyable at this time).
This only works for subclasses as long as their constructor
signature is compatible; the first argument must be the
optional default_factory, defaulting to None.
*/
PyObject *args;
PyObject *items;
PyObject *result;
if (dd->default_factory == NULL || dd->default_factory == Py_None)
args = PyTuple_New(0);
else
args = PyTuple_Pack(1, dd->default_factory);
if (args == NULL)
return NULL;
items = PyObject_CallMethod((PyObject *)dd, "iteritems", "()");
if (items == NULL) {
Py_DECREF(args);
return NULL;
}
result = PyTuple_Pack(5, dd->dict.ob_type, args,
Py_None, Py_None, items);
Py_DECREF(args);
return result;
}
static PyMethodDef defdict_methods[] = {
{"__missing__", (PyCFunction)defdict_missing, METH_O,
defdict_missing_doc},
{"copy", (PyCFunction)defdict_copy, METH_NOARGS,
defdict_copy_doc},
{"__copy__", (PyCFunction)defdict_copy, METH_NOARGS,
defdict_copy_doc},
{"__reduce__", (PyCFunction)defdict_reduce, METH_NOARGS,
reduce_doc},
{NULL}
};
static PyMemberDef defdict_members[] = {
{"default_factory", T_OBJECT,
offsetof(defdictobject, default_factory), 0,
PyDoc_STR("Factory for default value called by __missing__().")},
{NULL}
};
static void
defdict_dealloc(defdictobject *dd)
{
Py_CLEAR(dd->default_factory);
PyDict_Type.tp_dealloc((PyObject *)dd);
}
static int
defdict_print(defdictobject *dd, FILE *fp, int flags)
{
int sts;
fprintf(fp, "defaultdict(");
if (dd->default_factory == NULL)
fprintf(fp, "None");
else {
PyObject_Print(dd->default_factory, fp, 0);
}
fprintf(fp, ", ");
sts = PyDict_Type.tp_print((PyObject *)dd, fp, 0);
fprintf(fp, ")");
return sts;
}
static PyObject *
defdict_repr(defdictobject *dd)
{
PyObject *defrepr;
PyObject *baserepr;
PyObject *result;
baserepr = PyDict_Type.tp_repr((PyObject *)dd);
if (baserepr == NULL)
return NULL;
if (dd->default_factory == NULL)
defrepr = PyString_FromString("None");
else
defrepr = PyObject_Repr(dd->default_factory);
if (defrepr == NULL) {
Py_DECREF(baserepr);
return NULL;
}
result = PyString_FromFormat("defaultdict(%s, %s)",
PyString_AS_STRING(defrepr),
PyString_AS_STRING(baserepr));
Py_DECREF(defrepr);
Py_DECREF(baserepr);
return result;
}
static int
defdict_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(((defdictobject *)self)->default_factory);
return PyDict_Type.tp_traverse(self, visit, arg);
}
static int
defdict_tp_clear(defdictobject *dd)
{
if (dd->default_factory != NULL) {
Py_DECREF(dd->default_factory);
dd->default_factory = NULL;
}
return PyDict_Type.tp_clear((PyObject *)dd);
}
static int
defdict_init(PyObject *self, PyObject *args, PyObject *kwds)
{
defdictobject *dd = (defdictobject *)self;
PyObject *olddefault = dd->default_factory;
PyObject *newdefault = NULL;
PyObject *newargs;
int result;
if (args == NULL || !PyTuple_Check(args))
newargs = PyTuple_New(0);
else {
Py_ssize_t n = PyTuple_GET_SIZE(args);
if (n > 0)
newdefault = PyTuple_GET_ITEM(args, 0);
newargs = PySequence_GetSlice(args, 1, n);
}
if (newargs == NULL)
return -1;
Py_XINCREF(newdefault);
dd->default_factory = newdefault;
result = PyDict_Type.tp_init(self, newargs, kwds);
Py_DECREF(newargs);
Py_XDECREF(olddefault);
return result;
}
PyDoc_STRVAR(defdict_doc,
"defaultdict(default_factory) --> dict with default factory\n\
\n\
The default factory is called without arguments to produce\n\
a new value when a key is not present, in __getitem__ only.\n\
A defaultdict compares equal to a dict with the same items.\n\
");
static PyTypeObject defdict_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"collections.defaultdict", /* tp_name */
sizeof(defdictobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)defdict_dealloc, /* tp_dealloc */
(printfunc)defdict_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
(reprfunc)defdict_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */
defdict_doc, /* tp_doc */
(traverseproc)defdict_traverse, /* tp_traverse */
(inquiry)defdict_tp_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset*/
0, /* tp_iter */
0, /* tp_iternext */
defdict_methods, /* tp_methods */
defdict_members, /* tp_members */
0, /* tp_getset */
&PyDict_Type, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)defdict_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
0, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
/* module level code ********************************************************/ /* module level code ********************************************************/
PyDoc_STRVAR(module_doc, PyDoc_STRVAR(module_doc,
"High performance data structures\n\ "High performance data structures.\n\
- deque: ordered collection accessible from endpoints only\n\
- defaultdict: dict subclass with a default value factory\n\
"); ");
PyMODINIT_FUNC PyMODINIT_FUNC
...@@ -1085,6 +1344,11 @@ initcollections(void) ...@@ -1085,6 +1344,11 @@ initcollections(void)
Py_INCREF(&deque_type); Py_INCREF(&deque_type);
PyModule_AddObject(m, "deque", (PyObject *)&deque_type); PyModule_AddObject(m, "deque", (PyObject *)&deque_type);
if (PyType_Ready(&defdict_type) < 0)
return;
Py_INCREF(&defdict_type);
PyModule_AddObject(m, "defaultdict", (PyObject *)&defdict_type);
if (PyType_Ready(&dequeiter_type) < 0) if (PyType_Ready(&dequeiter_type) < 0)
return; return;
......
...@@ -882,8 +882,22 @@ dict_subscript(dictobject *mp, register PyObject *key) ...@@ -882,8 +882,22 @@ dict_subscript(dictobject *mp, register PyObject *key)
return NULL; return NULL;
} }
v = (mp->ma_lookup)(mp, key, hash) -> me_value; v = (mp->ma_lookup)(mp, key, hash) -> me_value;
if (v == NULL) if (v == NULL) {
if (!PyDict_CheckExact(mp)) {
/* Look up __missing__ method if we're a subclass. */
static PyObject *missing_str = NULL;
if (missing_str == NULL)
missing_str =
PyString_InternFromString("__missing__");
PyObject *missing = _PyType_Lookup(mp->ob_type,
missing_str);
if (missing != NULL)
return PyObject_CallFunctionObjArgs(missing,
(PyObject *)mp, key, NULL);
}
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
else else
Py_INCREF(v); Py_INCREF(v);
return v; return v;
......
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