mirror of
https://github.com/python/cpython.git
synced 2025-09-27 18:59:43 +00:00
Issue #22609: Constructors and update methods of mapping classes in the
collections module now accept the self keyword argument.
This commit is contained in:
parent
4847035458
commit
ae5cb214d2
4 changed files with 121 additions and 28 deletions
|
@ -584,23 +584,24 @@ class MutableMapping(Mapping):
|
||||||
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
|
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
|
||||||
In either case, this is followed by: for k, v in F.items(): D[k] = v
|
In either case, this is followed by: for k, v in F.items(): D[k] = v
|
||||||
'''
|
'''
|
||||||
if len(args) > 2:
|
if not args:
|
||||||
raise TypeError("update() takes at most 2 positional "
|
raise TypeError("descriptor 'update' of 'MutableMapping' object "
|
||||||
"arguments ({} given)".format(len(args)))
|
"needs an argument")
|
||||||
elif not args:
|
self, *args = args
|
||||||
raise TypeError("update() takes at least 1 argument (0 given)")
|
if len(args) > 1:
|
||||||
self = args[0]
|
raise TypeError('update expected at most 1 arguments, got %d' %
|
||||||
other = args[1] if len(args) >= 2 else ()
|
len(args))
|
||||||
|
if args:
|
||||||
if isinstance(other, Mapping):
|
other = args[0]
|
||||||
for key in other:
|
if isinstance(other, Mapping):
|
||||||
self[key] = other[key]
|
for key in other:
|
||||||
elif hasattr(other, "keys"):
|
self[key] = other[key]
|
||||||
for key in other.keys():
|
elif hasattr(other, "keys"):
|
||||||
self[key] = other[key]
|
for key in other.keys():
|
||||||
else:
|
self[key] = other[key]
|
||||||
for key, value in other:
|
else:
|
||||||
self[key] = value
|
for key, value in other:
|
||||||
|
self[key] = value
|
||||||
for key, value in kwds.items():
|
for key, value in kwds.items():
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
|
|
|
@ -38,12 +38,16 @@ class OrderedDict(dict):
|
||||||
# Individual links are kept alive by the hard reference in self.__map.
|
# Individual links are kept alive by the hard reference in self.__map.
|
||||||
# Those hard references disappear when a key is deleted from an OrderedDict.
|
# Those hard references disappear when a key is deleted from an OrderedDict.
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(*args, **kwds):
|
||||||
'''Initialize an ordered dictionary. The signature is the same as
|
'''Initialize an ordered dictionary. The signature is the same as
|
||||||
regular dictionaries, but keyword arguments are not recommended because
|
regular dictionaries, but keyword arguments are not recommended because
|
||||||
their insertion order is arbitrary.
|
their insertion order is arbitrary.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
if not args:
|
||||||
|
raise TypeError("descriptor '__init__' of 'OrderedDict' object "
|
||||||
|
"needs an argument")
|
||||||
|
self, *args = args
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
try:
|
try:
|
||||||
|
@ -450,7 +454,7 @@ class Counter(dict):
|
||||||
# http://code.activestate.com/recipes/259174/
|
# http://code.activestate.com/recipes/259174/
|
||||||
# Knuth, TAOCP Vol. II section 4.6.3
|
# Knuth, TAOCP Vol. II section 4.6.3
|
||||||
|
|
||||||
def __init__(self, iterable=None, **kwds):
|
def __init__(*args, **kwds):
|
||||||
'''Create a new, empty Counter object. And if given, count elements
|
'''Create a new, empty Counter object. And if given, count elements
|
||||||
from an input iterable. Or, initialize the count from another mapping
|
from an input iterable. Or, initialize the count from another mapping
|
||||||
of elements to their counts.
|
of elements to their counts.
|
||||||
|
@ -461,8 +465,14 @@ class Counter(dict):
|
||||||
>>> c = Counter(a=4, b=2) # a new counter from keyword args
|
>>> c = Counter(a=4, b=2) # a new counter from keyword args
|
||||||
|
|
||||||
'''
|
'''
|
||||||
super().__init__()
|
if not args:
|
||||||
self.update(iterable, **kwds)
|
raise TypeError("descriptor '__init__' of 'Counter' object "
|
||||||
|
"needs an argument")
|
||||||
|
self, *args = args
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
super(Counter, self).__init__()
|
||||||
|
self.update(*args, **kwds)
|
||||||
|
|
||||||
def __missing__(self, key):
|
def __missing__(self, key):
|
||||||
'The count of elements not in the Counter is zero.'
|
'The count of elements not in the Counter is zero.'
|
||||||
|
@ -513,7 +523,7 @@ class Counter(dict):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Counter.fromkeys() is undefined. Use Counter(iterable) instead.')
|
'Counter.fromkeys() is undefined. Use Counter(iterable) instead.')
|
||||||
|
|
||||||
def update(self, iterable=None, **kwds):
|
def update(*args, **kwds):
|
||||||
'''Like dict.update() but add counts instead of replacing them.
|
'''Like dict.update() but add counts instead of replacing them.
|
||||||
|
|
||||||
Source can be an iterable, a dictionary, or another Counter instance.
|
Source can be an iterable, a dictionary, or another Counter instance.
|
||||||
|
@ -533,6 +543,13 @@ class Counter(dict):
|
||||||
# contexts. Instead, we implement straight-addition. Both the inputs
|
# contexts. Instead, we implement straight-addition. Both the inputs
|
||||||
# and outputs are allowed to contain zero and negative counts.
|
# and outputs are allowed to contain zero and negative counts.
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
raise TypeError("descriptor 'update' of 'Counter' object "
|
||||||
|
"needs an argument")
|
||||||
|
self, *args = args
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
iterable = args[0] if args else None
|
||||||
if iterable is not None:
|
if iterable is not None:
|
||||||
if isinstance(iterable, Mapping):
|
if isinstance(iterable, Mapping):
|
||||||
if self:
|
if self:
|
||||||
|
@ -540,13 +557,13 @@ class Counter(dict):
|
||||||
for elem, count in iterable.items():
|
for elem, count in iterable.items():
|
||||||
self[elem] = count + self_get(elem, 0)
|
self[elem] = count + self_get(elem, 0)
|
||||||
else:
|
else:
|
||||||
super().update(iterable) # fast path when counter is empty
|
super(Counter, self).update(iterable) # fast path when counter is empty
|
||||||
else:
|
else:
|
||||||
_count_elements(self, iterable)
|
_count_elements(self, iterable)
|
||||||
if kwds:
|
if kwds:
|
||||||
self.update(kwds)
|
self.update(kwds)
|
||||||
|
|
||||||
def subtract(self, iterable=None, **kwds):
|
def subtract(*args, **kwds):
|
||||||
'''Like dict.update() but subtracts counts instead of replacing them.
|
'''Like dict.update() but subtracts counts instead of replacing them.
|
||||||
Counts can be reduced below zero. Both the inputs and outputs are
|
Counts can be reduced below zero. Both the inputs and outputs are
|
||||||
allowed to contain zero and negative counts.
|
allowed to contain zero and negative counts.
|
||||||
|
@ -562,6 +579,13 @@ class Counter(dict):
|
||||||
-1
|
-1
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
if not args:
|
||||||
|
raise TypeError("descriptor 'subtract' of 'Counter' object "
|
||||||
|
"needs an argument")
|
||||||
|
self, *args = args
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
iterable = args[0] if args else None
|
||||||
if iterable is not None:
|
if iterable is not None:
|
||||||
self_get = self.get
|
self_get = self.get
|
||||||
if isinstance(iterable, Mapping):
|
if isinstance(iterable, Mapping):
|
||||||
|
@ -869,7 +893,14 @@ class ChainMap(MutableMapping):
|
||||||
class UserDict(MutableMapping):
|
class UserDict(MutableMapping):
|
||||||
|
|
||||||
# Start by filling-out the abstract methods
|
# Start by filling-out the abstract methods
|
||||||
def __init__(self, dict=None, **kwargs):
|
def __init__(*args, **kwargs):
|
||||||
|
if not args:
|
||||||
|
raise TypeError("descriptor '__init__' of 'UserDict' object "
|
||||||
|
"needs an argument")
|
||||||
|
self, *args = args
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
dict = args[0] if args else None
|
||||||
self.data = {}
|
self.data = {}
|
||||||
if dict is not None:
|
if dict is not None:
|
||||||
self.update(dict)
|
self.update(dict)
|
||||||
|
|
|
@ -1084,6 +1084,28 @@ class TestCounter(unittest.TestCase):
|
||||||
self.assertEqual(c.setdefault('e', 5), 5)
|
self.assertEqual(c.setdefault('e', 5), 5)
|
||||||
self.assertEqual(c['e'], 5)
|
self.assertEqual(c['e'], 5)
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
self.assertEqual(list(Counter(self=42).items()), [('self', 42)])
|
||||||
|
self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)])
|
||||||
|
self.assertEqual(list(Counter(iterable=None).items()), [('iterable', None)])
|
||||||
|
self.assertRaises(TypeError, Counter, 42)
|
||||||
|
self.assertRaises(TypeError, Counter, (), ())
|
||||||
|
self.assertRaises(TypeError, Counter.__init__)
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
c = Counter()
|
||||||
|
c.update(self=42)
|
||||||
|
self.assertEqual(list(c.items()), [('self', 42)])
|
||||||
|
c = Counter()
|
||||||
|
c.update(iterable=42)
|
||||||
|
self.assertEqual(list(c.items()), [('iterable', 42)])
|
||||||
|
c = Counter()
|
||||||
|
c.update(iterable=None)
|
||||||
|
self.assertEqual(list(c.items()), [('iterable', None)])
|
||||||
|
self.assertRaises(TypeError, Counter().update, 42)
|
||||||
|
self.assertRaises(TypeError, Counter().update, {}, {})
|
||||||
|
self.assertRaises(TypeError, Counter.update)
|
||||||
|
|
||||||
def test_copying(self):
|
def test_copying(self):
|
||||||
# Check that counters are copyable, deepcopyable, picklable, and
|
# Check that counters are copyable, deepcopyable, picklable, and
|
||||||
#have a repr/eval round-trip
|
#have a repr/eval round-trip
|
||||||
|
@ -1205,6 +1227,16 @@ class TestCounter(unittest.TestCase):
|
||||||
c.subtract('aaaabbcce')
|
c.subtract('aaaabbcce')
|
||||||
self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
|
self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
|
||||||
|
|
||||||
|
c = Counter()
|
||||||
|
c.subtract(self=42)
|
||||||
|
self.assertEqual(list(c.items()), [('self', -42)])
|
||||||
|
c = Counter()
|
||||||
|
c.subtract(iterable=42)
|
||||||
|
self.assertEqual(list(c.items()), [('iterable', -42)])
|
||||||
|
self.assertRaises(TypeError, Counter().subtract, 42)
|
||||||
|
self.assertRaises(TypeError, Counter().subtract, {}, {})
|
||||||
|
self.assertRaises(TypeError, Counter.subtract)
|
||||||
|
|
||||||
def test_unary(self):
|
def test_unary(self):
|
||||||
c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
|
c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
|
||||||
self.assertEqual(dict(+c), dict(c=5, d=10, e=15, g=40))
|
self.assertEqual(dict(+c), dict(c=5, d=10, e=15, g=40))
|
||||||
|
@ -1255,8 +1287,11 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
c=3, e=5).items()), pairs) # mixed input
|
c=3, e=5).items()), pairs) # mixed input
|
||||||
|
|
||||||
# make sure no positional args conflict with possible kwdargs
|
# make sure no positional args conflict with possible kwdargs
|
||||||
self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__']).args,
|
self.assertEqual(list(OrderedDict(self=42).items()), [('self', 42)])
|
||||||
['self'])
|
self.assertEqual(list(OrderedDict(other=42).items()), [('other', 42)])
|
||||||
|
self.assertRaises(TypeError, OrderedDict, 42)
|
||||||
|
self.assertRaises(TypeError, OrderedDict, (), ())
|
||||||
|
self.assertRaises(TypeError, OrderedDict.__init__)
|
||||||
|
|
||||||
# Make sure that direct calls to __init__ do not clear previous contents
|
# Make sure that direct calls to __init__ do not clear previous contents
|
||||||
d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
|
d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
|
||||||
|
@ -1301,6 +1336,10 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(list(d.items()),
|
self.assertEqual(list(d.items()),
|
||||||
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
|
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, OrderedDict().update, 42)
|
||||||
|
self.assertRaises(TypeError, OrderedDict().update, (), ())
|
||||||
|
self.assertRaises(TypeError, OrderedDict.update)
|
||||||
|
|
||||||
def test_abc(self):
|
def test_abc(self):
|
||||||
self.assertIsInstance(OrderedDict(), MutableMapping)
|
self.assertIsInstance(OrderedDict(), MutableMapping)
|
||||||
self.assertTrue(issubclass(OrderedDict, MutableMapping))
|
self.assertTrue(issubclass(OrderedDict, MutableMapping))
|
||||||
|
@ -1532,6 +1571,24 @@ class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
|
||||||
d = self._empty_mapping()
|
d = self._empty_mapping()
|
||||||
self.assertRaises(KeyError, d.popitem)
|
self.assertRaises(KeyError, d.popitem)
|
||||||
|
|
||||||
|
class TestUserDict(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
self.assertEqual(list(UserDict(self=42).items()), [('self', 42)])
|
||||||
|
self.assertEqual(list(UserDict(dict=42).items()), [('dict', 42)])
|
||||||
|
self.assertEqual(list(UserDict(dict=None).items()), [('dict', None)])
|
||||||
|
self.assertRaises(TypeError, UserDict, 42)
|
||||||
|
self.assertRaises(TypeError, UserDict, (), ())
|
||||||
|
self.assertRaises(TypeError, UserDict.__init__)
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
d = UserDict()
|
||||||
|
d.update(self=42)
|
||||||
|
self.assertEqual(list(d.items()), [('self', 42)])
|
||||||
|
self.assertRaises(TypeError, UserDict().update, 42)
|
||||||
|
self.assertRaises(TypeError, UserDict().update, {}, {})
|
||||||
|
self.assertRaises(TypeError, UserDict.update)
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### Run tests
|
### Run tests
|
||||||
|
@ -1543,7 +1600,8 @@ def test_main(verbose=None):
|
||||||
NamedTupleDocs = doctest.DocTestSuite(module=collections)
|
NamedTupleDocs = doctest.DocTestSuite(module=collections)
|
||||||
test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
|
test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
|
||||||
TestCollectionABCs, TestCounter, TestChainMap,
|
TestCollectionABCs, TestCounter, TestChainMap,
|
||||||
TestOrderedDict, GeneralMappingTests, SubclassMappingTests]
|
TestOrderedDict, GeneralMappingTests, SubclassMappingTests,
|
||||||
|
TestUserDict,]
|
||||||
support.run_unittest(*test_classes)
|
support.run_unittest(*test_classes)
|
||||||
support.run_doctest(collections, verbose)
|
support.run_doctest(collections, verbose)
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #22609: Constructors and update methods of mapping classes in the
|
||||||
|
collections module now accept the self keyword argument.
|
||||||
|
|
||||||
- Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
|
- Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
|
||||||
|
|
||||||
- Issue #22921: Allow SSLContext to take the *hostname* parameter even if
|
- Issue #22921: Allow SSLContext to take the *hostname* parameter even if
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue