Skip to content

Commit

Permalink
SF #904720: dict.update should take a 2-tuple sequence like dict.__init_
Browse files Browse the repository at this point in the history
(Championed by Bob Ippolito.)

The update() method for mappings now accepts all the same argument forms
as the dict() constructor.  This includes item lists and/or keyword
arguments.
  • Loading branch information
rhettinger committed Mar 4, 2004
1 parent 6c79a51 commit 31017ae
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 55 deletions.
13 changes: 10 additions & 3 deletions Doc/lib/libstdtypes.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1266,9 +1266,9 @@ \subsection{Mapping Types \label{typesmapping}}
{a copy of \var{a}'s list of (\var{key}, \var{value}) pairs}
{(3)}
\lineiii{\var{a}.keys()}{a copy of \var{a}'s list of keys}{(3)}
\lineiii{\var{a}.update(\var{b})}
{\code{for \var{k} in \var{b}.keys(): \var{a}[\var{k}] = \var{b}[\var{k}]}}
{}
\lineiii{\var{a}.update(\optional{\var{b}})}
{updates (and overwrites) key/value pairs from \var{b}}
{(9)}
\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)}
Expand Down Expand Up @@ -1338,6 +1338,13 @@ \subsection{Mapping Types \label{typesmapping}}
value is given and the key is not found. \versionadded{2.3}
\end{description}

\item[(9)] \function{update()} accepts either another mapping object
or an iterable of key/value pairs (as a tuple or other iterable of
length two). If keyword arguments are specified, the mapping is
then is updated with those key/value pairs:
\samp{d.update(red=1, blue=2)}.
\versionchanged[Allowed the argument to be an iterable of key/value
pairs and allowed keyword arguments]{2.4}

\subsection{File Objects
\label{bltin-file-objects}}
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/whatsnew24.tex
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ \section{Other Language Changes}

\begin{itemize}

\item The \method{dict.update()} method now accepts the same
argument forms as the \class{dict} constructor. This includes any
mapping, any iterable of key/value pairs, and/or keyword arguments.

\item The string methods, \method{ljust()}, \method{rjust()}, and
\method{center()} now take an optional argument for specifying a
fill character other than a space.
Expand Down
28 changes: 17 additions & 11 deletions Lib/UserDict.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ class UserDict:
def __init__(self, dict=None, **kwargs):
self.data = {}
if dict is not None:
if not hasattr(dict,'keys'):
dict = type({})(dict) # make mapping from a sequence
self.update(dict)
if len(kwargs):
self.update(kwargs)
Expand Down Expand Up @@ -39,14 +37,18 @@ def iterkeys(self): return self.data.iterkeys()
def itervalues(self): return self.data.itervalues()
def values(self): return self.data.values()
def has_key(self, key): return self.data.has_key(key)
def update(self, dict):
if isinstance(dict, UserDict):
def update(self, dict=None, **kwargs):
if dict is None:
pass
elif isinstance(dict, UserDict):
self.data.update(dict.data)
elif isinstance(dict, type(self.data)):
elif isinstance(dict, type({})) or not hasattr(dict, 'items'):
self.data.update(dict)
else:
for k, v in dict.items():
self[k] = v
if len(kwargs):
self.data.update(kwargs)
def get(self, key, failobj=None):
if not self.has_key(key):
return failobj
Expand Down Expand Up @@ -136,17 +138,21 @@ def popitem(self):
raise KeyError, 'container is empty'
del self[k]
return (k, v)
def update(self, other):
def update(self, other=None, **kwargs):
# Make progressively weaker assumptions about "other"
if hasattr(other, 'iteritems'): # iteritems saves memory and lookups
if other is None:
pass
elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups
for k, v in other.iteritems():
self[k] = v
elif hasattr(other, '__iter__'): # iter saves memory
for k in other:
self[k] = other[k]
else:
elif hasattr(other, 'keys'):
for k in other.keys():
self[k] = other[k]
else:
for k, v in other:
self[k] = v
if kwargs:
self.update(kwargs)
def get(self, key, default=None):
try:
return self[key]
Expand Down
6 changes: 0 additions & 6 deletions Lib/os.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,6 @@ def __contains__(self, key):
return key.upper() in self.data
def get(self, key, failobj=None):
return self.data.get(key.upper(), failobj)
def update(self, dict):
for k, v in dict.items():
self[k] = v
def copy(self):
return dict(self)

Expand All @@ -447,9 +444,6 @@ def __init__(self, environ):
def __setitem__(self, key, item):
putenv(key, item)
self.data[key] = item
def update(self, dict):
for k, v in dict.items():
self[k] = v
try:
unsetenv
except NameError:
Expand Down
18 changes: 9 additions & 9 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,41 +86,41 @@ def test_oldargs0_2_kw(self):
self.assertRaises(TypeError, {}.keys, x=2, y=2)

def test_oldargs1_0(self):
self.assertRaises(TypeError, {}.update)
self.assertRaises(TypeError, [].count)

def test_oldargs1_1(self):
{}.update({})
[].count(1)

def test_oldargs1_2(self):
self.assertRaises(TypeError, {}.update, {}, 1)
self.assertRaises(TypeError, [].count, 1, 2)

def test_oldargs1_0_ext(self):
try:
{}.update(*())
[].count(*())
except TypeError:
pass
else:
raise RuntimeError

def test_oldargs1_1_ext(self):
{}.update(*({},))
[].count(*(1,))

def test_oldargs1_2_ext(self):
try:
{}.update(*({}, 2))
[].count(*(1, 2))
except TypeError:
pass
else:
raise RuntimeError

def test_oldargs1_0_kw(self):
self.assertRaises(TypeError, {}.update, x=2)
self.assertRaises(TypeError, [].count, x=2)

def test_oldargs1_1_kw(self):
self.assertRaises(TypeError, {}.update, {}, x=2)
self.assertRaises(TypeError, [].count, {}, x=2)

def test_oldargs1_2_kw(self):
self.assertRaises(TypeError, {}.update, x=2, y=2)
self.assertRaises(TypeError, [].count, x=2, y=2)


def test_main():
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ class C: pass
if d != {1:1, 2:2, 3:3}: raise TestFailed, 'dict update'
d.clear()
try: d.update(None)
except AttributeError: pass
except (TypeError, AttributeError): pass
else: raise TestFailed, 'dict.update(None), AttributeError expected'
class SimpleUserDict:
def __init__(self):
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_userdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,12 @@ def test_write(self):
#update
p.update(self.reference)
self.assertEqual(dict(p), self.reference)
items = p.items()
p = self._empty_mapping()
p.update(items)
self.assertEqual(dict(p), self.reference)
d = self._full_mapping(self.reference)
#setdefaullt
#setdefault
key, value = d.iteritems().next()
knownkey, knownvalue = self.other.iteritems().next()
self.assertEqual(d.setdefault(key, knownvalue), value)
Expand Down
22 changes: 16 additions & 6 deletions Lib/weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,15 @@ def setdefault(self, key, default):
else:
return wr()

def update(self, dict):
def update(self, dict=None, **kwargs):
d = self.data
for key, o in dict.items():
d[key] = ref(o, self.__makeremove(key))
if dict is not None:
if not hasattr(dict, "items"):
dict = type({})(dict)
for key, o in dict.items():
d[key] = ref(o, self.__makeremove(key))
if len(kwargs):
self.update(kwargs)

def values(self):
L = []
Expand Down Expand Up @@ -239,10 +244,15 @@ def pop(self, key, *args):
def setdefault(self, key, default):
return self.data.setdefault(ref(key, self._remove),default)

def update(self, dict):
def update(self, dict=None, **kwargs):
d = self.data
for key, value in dict.items():
d[ref(key, self._remove)] = value
if dict is not None:
if not hasattr(dict, "items"):
dict = type({})(dict)
for key, value in dict.items():
d[ref(key, self._remove)] = value
if len(kwargs):
self.update(kwargs)


class BaseIter:
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ Mihai Ibanescu
Juan David Ib��ez Palomar
Tony Ingraldi
John Interrante
Bob Ippolito
Ben Jackson
Paul Jackson
David Jacobs
Expand Down
4 changes: 4 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ Core and builtins
the overallocation is no more than three elements -- this improves space
utilization for applications that have large numbers of small lists.

- The dict.update() method now accepts all the same argument forms
as the dict() constructor. This now includes item lists and/or
keyword arguments.

- Support for arbitrary objects supporting the read-only buffer
interface as the co_code field of code objects (something that was
only possible to create from C code) has been removed.
Expand Down
42 changes: 24 additions & 18 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1029,10 +1029,30 @@ dict_fromkeys(PyObject *cls, PyObject *args)
return NULL;
}

static int
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname)
{
PyObject *arg = NULL;
int result = 0;

if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg))
result = -1;

else if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = PyDict_Merge(self, arg, 1);
else
result = PyDict_MergeFromSeq2(self, arg, 1);
}
if (result == 0 && kwds != NULL)
result = PyDict_Merge(self, kwds, 1);
return result;
}

static PyObject *
dict_update(PyObject *mp, PyObject *other)
dict_update(PyObject *self, PyObject *args, PyObject *kwds)
{
if (PyDict_Update(mp, other) < 0)
if (dict_update_common(self, args, kwds, "update") == -1)
return NULL;
Py_INCREF(Py_None);
return Py_None;
Expand Down Expand Up @@ -1806,7 +1826,7 @@ static PyMethodDef mapp_methods[] = {
items__doc__},
{"values", (PyCFunction)dict_values, METH_NOARGS,
values__doc__},
{"update", (PyCFunction)dict_update, METH_O,
{"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
update__doc__},
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
fromkeys__doc__},
Expand Down Expand Up @@ -1875,21 +1895,7 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
dict_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *arg = NULL;
int result = 0;

if (!PyArg_UnpackTuple(args, "dict", 0, 1, &arg))
result = -1;

else if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = PyDict_Merge(self, arg, 1);
else
result = PyDict_MergeFromSeq2(self, arg, 1);
}
if (result == 0 && kwds != NULL)
result = PyDict_Merge(self, kwds, 1);
return result;
return dict_update_common(self, args, kwds, "dict");
}

static long
Expand Down

0 comments on commit 31017ae

Please sign in to comment.