mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
SF #904720: dict.update should take a 2-tuple sequence like dict.__init_
(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.
This commit is contained in:
parent
6c79a518e7
commit
31017aed36
11 changed files with 92 additions and 56 deletions
|
@ -1266,9 +1266,9 @@ arbitrary objects):
|
||||||
{a copy of \var{a}'s list of (\var{key}, \var{value}) pairs}
|
{a copy of \var{a}'s list of (\var{key}, \var{value}) pairs}
|
||||||
{(3)}
|
{(3)}
|
||||||
\lineiii{\var{a}.keys()}{a copy of \var{a}'s list of keys}{(3)}
|
\lineiii{\var{a}.keys()}{a copy of \var{a}'s list of keys}{(3)}
|
||||||
\lineiii{\var{a}.update(\var{b})}
|
\lineiii{\var{a}.update(\optional{\var{b}})}
|
||||||
{\code{for \var{k} in \var{b}.keys(): \var{a}[\var{k}] = \var{b}[\var{k}]}}
|
{updates (and overwrites) key/value pairs from \var{b}}
|
||||||
{}
|
{(9)}
|
||||||
\lineiii{\var{a}.fromkeys(\var{seq}\optional{, \var{value}})}
|
\lineiii{\var{a}.fromkeys(\var{seq}\optional{, \var{value}})}
|
||||||
{Creates a new dictionary with keys from \var{seq} and values set to \var{value}}
|
{Creates a new dictionary with keys from \var{seq} and values set to \var{value}}
|
||||||
{(7)}
|
{(7)}
|
||||||
|
@ -1338,6 +1338,13 @@ new dictionary. \var{value} defaults to \code{None}. \versionadded{2.3}
|
||||||
value is given and the key is not found. \versionadded{2.3}
|
value is given and the key is not found. \versionadded{2.3}
|
||||||
\end{description}
|
\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
|
\subsection{File Objects
|
||||||
\label{bltin-file-objects}}
|
\label{bltin-file-objects}}
|
||||||
|
|
|
@ -134,6 +134,10 @@ language.
|
||||||
|
|
||||||
\begin{itemize}
|
\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
|
\item The string methods, \method{ljust()}, \method{rjust()}, and
|
||||||
\method{center()} now take an optional argument for specifying a
|
\method{center()} now take an optional argument for specifying a
|
||||||
fill character other than a space.
|
fill character other than a space.
|
||||||
|
|
|
@ -4,8 +4,6 @@ class UserDict:
|
||||||
def __init__(self, dict=None, **kwargs):
|
def __init__(self, dict=None, **kwargs):
|
||||||
self.data = {}
|
self.data = {}
|
||||||
if dict is not None:
|
if dict is not None:
|
||||||
if not hasattr(dict,'keys'):
|
|
||||||
dict = type({})(dict) # make mapping from a sequence
|
|
||||||
self.update(dict)
|
self.update(dict)
|
||||||
if len(kwargs):
|
if len(kwargs):
|
||||||
self.update(kwargs)
|
self.update(kwargs)
|
||||||
|
@ -39,14 +37,18 @@ class UserDict:
|
||||||
def itervalues(self): return self.data.itervalues()
|
def itervalues(self): return self.data.itervalues()
|
||||||
def values(self): return self.data.values()
|
def values(self): return self.data.values()
|
||||||
def has_key(self, key): return self.data.has_key(key)
|
def has_key(self, key): return self.data.has_key(key)
|
||||||
def update(self, dict):
|
def update(self, dict=None, **kwargs):
|
||||||
if isinstance(dict, UserDict):
|
if dict is None:
|
||||||
|
pass
|
||||||
|
elif isinstance(dict, UserDict):
|
||||||
self.data.update(dict.data)
|
self.data.update(dict.data)
|
||||||
elif isinstance(dict, type(self.data)):
|
elif isinstance(dict, type({})) or not hasattr(dict, 'items'):
|
||||||
self.data.update(dict)
|
self.data.update(dict)
|
||||||
else:
|
else:
|
||||||
for k, v in dict.items():
|
for k, v in dict.items():
|
||||||
self[k] = v
|
self[k] = v
|
||||||
|
if len(kwargs):
|
||||||
|
self.data.update(kwargs)
|
||||||
def get(self, key, failobj=None):
|
def get(self, key, failobj=None):
|
||||||
if not self.has_key(key):
|
if not self.has_key(key):
|
||||||
return failobj
|
return failobj
|
||||||
|
@ -136,17 +138,21 @@ class DictMixin:
|
||||||
raise KeyError, 'container is empty'
|
raise KeyError, 'container is empty'
|
||||||
del self[k]
|
del self[k]
|
||||||
return (k, v)
|
return (k, v)
|
||||||
def update(self, other):
|
def update(self, other=None, **kwargs):
|
||||||
# Make progressively weaker assumptions about "other"
|
# 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():
|
for k, v in other.iteritems():
|
||||||
self[k] = v
|
self[k] = v
|
||||||
elif hasattr(other, '__iter__'): # iter saves memory
|
elif hasattr(other, 'keys'):
|
||||||
for k in other:
|
|
||||||
self[k] = other[k]
|
|
||||||
else:
|
|
||||||
for k in other.keys():
|
for k in other.keys():
|
||||||
self[k] = other[k]
|
self[k] = other[k]
|
||||||
|
else:
|
||||||
|
for k, v in other:
|
||||||
|
self[k] = v
|
||||||
|
if kwargs:
|
||||||
|
self.update(kwargs)
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
try:
|
try:
|
||||||
return self[key]
|
return self[key]
|
||||||
|
|
|
@ -433,9 +433,6 @@ else:
|
||||||
return key.upper() in self.data
|
return key.upper() in self.data
|
||||||
def get(self, key, failobj=None):
|
def get(self, key, failobj=None):
|
||||||
return self.data.get(key.upper(), failobj)
|
return self.data.get(key.upper(), failobj)
|
||||||
def update(self, dict):
|
|
||||||
for k, v in dict.items():
|
|
||||||
self[k] = v
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return dict(self)
|
return dict(self)
|
||||||
|
|
||||||
|
@ -447,9 +444,6 @@ else:
|
||||||
def __setitem__(self, key, item):
|
def __setitem__(self, key, item):
|
||||||
putenv(key, item)
|
putenv(key, item)
|
||||||
self.data[key] = item
|
self.data[key] = item
|
||||||
def update(self, dict):
|
|
||||||
for k, v in dict.items():
|
|
||||||
self[k] = v
|
|
||||||
try:
|
try:
|
||||||
unsetenv
|
unsetenv
|
||||||
except NameError:
|
except NameError:
|
||||||
|
|
|
@ -86,41 +86,41 @@ class CFunctionCalls(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, {}.keys, x=2, y=2)
|
self.assertRaises(TypeError, {}.keys, x=2, y=2)
|
||||||
|
|
||||||
def test_oldargs1_0(self):
|
def test_oldargs1_0(self):
|
||||||
self.assertRaises(TypeError, {}.update)
|
self.assertRaises(TypeError, [].count)
|
||||||
|
|
||||||
def test_oldargs1_1(self):
|
def test_oldargs1_1(self):
|
||||||
{}.update({})
|
[].count(1)
|
||||||
|
|
||||||
def test_oldargs1_2(self):
|
def test_oldargs1_2(self):
|
||||||
self.assertRaises(TypeError, {}.update, {}, 1)
|
self.assertRaises(TypeError, [].count, 1, 2)
|
||||||
|
|
||||||
def test_oldargs1_0_ext(self):
|
def test_oldargs1_0_ext(self):
|
||||||
try:
|
try:
|
||||||
{}.update(*())
|
[].count(*())
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
|
|
||||||
def test_oldargs1_1_ext(self):
|
def test_oldargs1_1_ext(self):
|
||||||
{}.update(*({},))
|
[].count(*(1,))
|
||||||
|
|
||||||
def test_oldargs1_2_ext(self):
|
def test_oldargs1_2_ext(self):
|
||||||
try:
|
try:
|
||||||
{}.update(*({}, 2))
|
[].count(*(1, 2))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
|
|
||||||
def test_oldargs1_0_kw(self):
|
def test_oldargs1_0_kw(self):
|
||||||
self.assertRaises(TypeError, {}.update, x=2)
|
self.assertRaises(TypeError, [].count, x=2)
|
||||||
|
|
||||||
def test_oldargs1_1_kw(self):
|
def test_oldargs1_1_kw(self):
|
||||||
self.assertRaises(TypeError, {}.update, {}, x=2)
|
self.assertRaises(TypeError, [].count, {}, x=2)
|
||||||
|
|
||||||
def test_oldargs1_2_kw(self):
|
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():
|
def test_main():
|
||||||
|
|
|
@ -253,7 +253,7 @@ d.update({1:1, 2:2, 3:3})
|
||||||
if d != {1:1, 2:2, 3:3}: raise TestFailed, 'dict update'
|
if d != {1:1, 2:2, 3:3}: raise TestFailed, 'dict update'
|
||||||
d.clear()
|
d.clear()
|
||||||
try: d.update(None)
|
try: d.update(None)
|
||||||
except AttributeError: pass
|
except (TypeError, AttributeError): pass
|
||||||
else: raise TestFailed, 'dict.update(None), AttributeError expected'
|
else: raise TestFailed, 'dict.update(None), AttributeError expected'
|
||||||
class SimpleUserDict:
|
class SimpleUserDict:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -93,8 +93,12 @@ class TestMappingProtocol(unittest.TestCase):
|
||||||
#update
|
#update
|
||||||
p.update(self.reference)
|
p.update(self.reference)
|
||||||
self.assertEqual(dict(p), 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)
|
d = self._full_mapping(self.reference)
|
||||||
#setdefaullt
|
#setdefault
|
||||||
key, value = d.iteritems().next()
|
key, value = d.iteritems().next()
|
||||||
knownkey, knownvalue = self.other.iteritems().next()
|
knownkey, knownvalue = self.other.iteritems().next()
|
||||||
self.assertEqual(d.setdefault(key, knownvalue), value)
|
self.assertEqual(d.setdefault(key, knownvalue), value)
|
||||||
|
|
|
@ -122,10 +122,15 @@ class WeakValueDictionary(UserDict.UserDict):
|
||||||
else:
|
else:
|
||||||
return wr()
|
return wr()
|
||||||
|
|
||||||
def update(self, dict):
|
def update(self, dict=None, **kwargs):
|
||||||
d = self.data
|
d = self.data
|
||||||
for key, o in dict.items():
|
if dict is not None:
|
||||||
d[key] = ref(o, self.__makeremove(key))
|
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):
|
def values(self):
|
||||||
L = []
|
L = []
|
||||||
|
@ -239,10 +244,15 @@ class WeakKeyDictionary(UserDict.UserDict):
|
||||||
def setdefault(self, key, default):
|
def setdefault(self, key, default):
|
||||||
return self.data.setdefault(ref(key, self._remove),default)
|
return self.data.setdefault(ref(key, self._remove),default)
|
||||||
|
|
||||||
def update(self, dict):
|
def update(self, dict=None, **kwargs):
|
||||||
d = self.data
|
d = self.data
|
||||||
for key, value in dict.items():
|
if dict is not None:
|
||||||
d[ref(key, self._remove)] = value
|
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:
|
class BaseIter:
|
||||||
|
|
|
@ -273,6 +273,7 @@ Mihai Ibanescu
|
||||||
Juan David Ibáñez Palomar
|
Juan David Ibáñez Palomar
|
||||||
Tony Ingraldi
|
Tony Ingraldi
|
||||||
John Interrante
|
John Interrante
|
||||||
|
Bob Ippolito
|
||||||
Ben Jackson
|
Ben Jackson
|
||||||
Paul Jackson
|
Paul Jackson
|
||||||
David Jacobs
|
David Jacobs
|
||||||
|
|
|
@ -32,6 +32,10 @@ Core and builtins
|
||||||
the overallocation is no more than three elements -- this improves space
|
the overallocation is no more than three elements -- this improves space
|
||||||
utilization for applications that have large numbers of small lists.
|
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
|
- Support for arbitrary objects supporting the read-only buffer
|
||||||
interface as the co_code field of code objects (something that was
|
interface as the co_code field of code objects (something that was
|
||||||
only possible to create from C code) has been removed.
|
only possible to create from C code) has been removed.
|
||||||
|
|
|
@ -1029,10 +1029,30 @@ Fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static int
|
||||||
dict_update(PyObject *mp, PyObject *other)
|
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname)
|
||||||
{
|
{
|
||||||
if (PyDict_Update(mp, other) < 0)
|
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 *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (dict_update_common(self, args, kwds, "update") == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
|
@ -1806,7 +1826,7 @@ static PyMethodDef mapp_methods[] = {
|
||||||
items__doc__},
|
items__doc__},
|
||||||
{"values", (PyCFunction)dict_values, METH_NOARGS,
|
{"values", (PyCFunction)dict_values, METH_NOARGS,
|
||||||
values__doc__},
|
values__doc__},
|
||||||
{"update", (PyCFunction)dict_update, METH_O,
|
{"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
|
||||||
update__doc__},
|
update__doc__},
|
||||||
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
|
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
|
||||||
fromkeys__doc__},
|
fromkeys__doc__},
|
||||||
|
@ -1875,21 +1895,7 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static int
|
static int
|
||||||
dict_init(PyObject *self, PyObject *args, PyObject *kwds)
|
dict_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyObject *arg = NULL;
|
return dict_update_common(self, args, kwds, "dict");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue