From e33d3df03079d704edbe159be079dc387058f044 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 27 Nov 2002 07:29:33 +0000 Subject: SF Patch 643443. Added dict.fromkeys(iterable, value=None), a class method for constructing new dictionaries from sequences of keys. --- Doc/lib/libstdtypes.tex | 9 +++++++- Lib/test/test_types.py | 28 +++++++++++++++++++++++++ Misc/NEWS | 5 +++++ Objects/dictobject.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index eb12aa7..6f9e453 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -1055,8 +1055,11 @@ arbitrary objects): {(3)} \lineiii{\var{a}.keys()}{a copy of \var{a}'s list of keys}{(3)} \lineiii{\var{a}.update(\var{b})} - {\code{for k in \var{b}.keys(): \var{a}[k] = \var{b}[k]}} + {\code{for \var{k} in \var{b}.keys(): \var{a}[\var{k}] = \var{b}[\var{k}]}} {} + \lineiii{\var{a}.fromkeys(\var{seq}\optional{, \var{value}})} + {Creates a new dictionary with keys from \var{seq} and values set to \var{value}} + {(7)} \lineiii{\var{a}.values()}{a copy of \var{a}'s list of values}{(3)} \lineiii{\var{a}.get(\var{k}\optional{, \var{x}})} {\code{\var{a}[\var{k}]} if \code{\var{k} in \var{a}}, @@ -1116,6 +1119,10 @@ the dictionary as the value of \var{k}. over a dictionary, as often used in set algorithms. \end{description} +\item[(7)] \function{fromkeys()} is a class method that returns a +new dictionary. \var{value} defaults to \code{None}. \versionadded{2.3} +\end{description} + \subsection{File Objects \label{bltin-file-objects}} diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index c20af2e..9cfc680 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -530,6 +530,34 @@ class FailingUserDict: try: d.update(FailingUserDict()) except ValueError: pass else: raise TestFailed, 'dict.update(), __getitem__ expected ValueError' +# dict.fromkeys() +if dict.fromkeys('abc') != {'a':None, 'b':None, 'c':None}: + raise TestFailed, 'dict.fromkeys did not work as a class method' +d = {} +if d.fromkeys('abc') is d: + raise TestFailed, 'dict.fromkeys did not return a new dict' +if d.fromkeys('abc') != {'a':None, 'b':None, 'c':None}: + raise TestFailed, 'dict.fromkeys failed with default value' +if d.fromkeys((4,5),0) != {4:0, 5:0}: + raise TestFailed, 'dict.fromkeys failed with specified value' +if d.fromkeys([]) != {}: + raise TestFailed, 'dict.fromkeys failed with null sequence' +def g(): + yield 1 +if d.fromkeys(g()) != {1:None}: + raise TestFailed, 'dict.fromkeys failed with a generator' +try: {}.fromkeys(3) +except TypeError: pass +else: raise TestFailed, 'dict.fromkeys failed to raise TypeError' +class dictlike(dict): pass +if dictlike.fromkeys('a') != {'a':None}: + raise TestFailed, 'dictsubclass.fromkeys did not inherit' +if dictlike().fromkeys('a') != {'a':None}: + raise TestFailed, 'dictsubclass.fromkeys did not inherit' +if type(dictlike.fromkeys('a')) is not dictlike: + raise TestFailed, 'dictsubclass.fromkeys created wrong type' +if type(dictlike().fromkeys('a')) is not dictlike: + raise TestFailed, 'dictsubclass.fromkeys created wrong type' # dict.copy() d = {1:1, 2:2, 3:3} if d.copy() != {1:1, 2:2, 3:3}: raise TestFailed, 'dict copy' diff --git a/Misc/NEWS b/Misc/NEWS index 89303ba..066f8a5 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -270,6 +270,11 @@ Core and builtins an optional argument that specifies the characters to strip. For example, "Foo!!!?!?!?".rstrip("?!") -> "Foo". +- Dictionaries have a new class method, fromkeys(iterable, value=None). + It constructs a new dictionary with keys taken from the iterable and + all values set to a single value. It is used for building membership + tests and for removing duplicates from sequences. + - Added a new dict method pop(key). This removes and returns the value corresponding to key. [SF patch #539949] diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2eaa20c..b8ba7e1 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -963,6 +963,56 @@ dict_items(register dictobject *mp) } static PyObject * +dict_fromkeys(PyObject *mp, PyObject *args) +{ + PyObject *seq; + PyObject *value = Py_None; + PyObject *it; /* iter(seq) */ + PyObject *key; + PyObject *d; + PyObject *cls; + int status; + + if (!PyArg_ParseTuple(args, "OO|O:fromkeys", &cls, &seq, &value)) + return NULL; + + d = PyObject_CallObject(cls, NULL); + if (d == NULL) + return NULL; + if (!PyDict_Check(d)) { + PyErr_BadInternalCall(); + return NULL; + } + + it = PyObject_GetIter(seq); + if (it == NULL){ + Py_DECREF(d); + return NULL; + } + + for (;;) { + key = PyIter_Next(it); + if (key == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + status = PyDict_SetItem(d, key, value); + Py_DECREF(key); + if (status < 0) + goto Fail; + } + + Py_DECREF(it); + return d; + +Fail: + Py_DECREF(it); + Py_DECREF(d); + return NULL; +} + +static PyObject * dict_update(PyObject *mp, PyObject *other) { if (PyDict_Update(mp, other) < 0) @@ -1682,6 +1732,10 @@ PyDoc_STRVAR(values__doc__, PyDoc_STRVAR(update__doc__, "D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]"); +PyDoc_STRVAR(fromkeys__doc__, +"dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n\ +v defaults to None."); + PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); @@ -1716,6 +1770,8 @@ static PyMethodDef mapp_methods[] = { values__doc__}, {"update", (PyCFunction)dict_update, METH_O, update__doc__}, + {"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS, + fromkeys__doc__}, {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, -- cgit v0.12