Commit 20994f1e authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #22609: Constructors and update methods of mapping classes in the

collections module now accept the self keyword argument.
parent 6ac2b4db
...@@ -548,23 +548,25 @@ class MutableMapping(Mapping): ...@@ -548,23 +548,25 @@ 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:
raise TypeError("update() takes at least 1 argument (0 given)")
self = args[0] self = args[0]
other = args[1] if len(args) >= 2 else () args = args[1:]
if len(args) > 1:
if isinstance(other, Mapping): raise TypeError('update expected at most 1 arguments, got %d' %
for key in other: len(args))
self[key] = other[key] if args:
elif hasattr(other, "keys"): other = args[0]
for key in other.keys(): if isinstance(other, Mapping):
self[key] = other[key] for key in other:
else: self[key] = other[key]
for key, value in other: elif hasattr(other, "keys"):
self[key] = value for key in other.keys():
self[key] = other[key]
else:
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
......
...@@ -35,12 +35,17 @@ class OrderedDict(dict): ...@@ -35,12 +35,17 @@ class OrderedDict(dict):
# The sentinel element never gets deleted (this simplifies the algorithm). # The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY]. # Each link is stored as a list of length three: [PREV, NEXT, KEY].
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[0]
args = args[1:]
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:
...@@ -438,7 +443,7 @@ class Counter(dict): ...@@ -438,7 +443,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.
...@@ -449,8 +454,15 @@ class Counter(dict): ...@@ -449,8 +454,15 @@ 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
''' '''
if not args:
raise TypeError("descriptor '__init__' of 'Counter' object "
"needs an argument")
self = args[0]
args = args[1:]
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
super(Counter, self).__init__() super(Counter, self).__init__()
self.update(iterable, **kwds) 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.'
...@@ -501,7 +513,7 @@ class Counter(dict): ...@@ -501,7 +513,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.
...@@ -521,6 +533,14 @@ class Counter(dict): ...@@ -521,6 +533,14 @@ 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[0]
args = args[1:]
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:
...@@ -536,7 +556,7 @@ class Counter(dict): ...@@ -536,7 +556,7 @@ class Counter(dict):
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.
...@@ -552,6 +572,14 @@ class Counter(dict): ...@@ -552,6 +572,14 @@ class Counter(dict):
-1 -1
''' '''
if not args:
raise TypeError("descriptor 'subtract' of 'Counter' object "
"needs an argument")
self = args[0]
args = args[1:]
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):
......
...@@ -905,6 +905,28 @@ class TestCounter(unittest.TestCase): ...@@ -905,6 +905,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
...@@ -1006,6 +1028,16 @@ class TestCounter(unittest.TestCase): ...@@ -1006,6 +1028,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)
class TestOrderedDict(unittest.TestCase): class TestOrderedDict(unittest.TestCase):
def test_init(self): def test_init(self):
...@@ -1019,8 +1051,11 @@ class TestOrderedDict(unittest.TestCase): ...@@ -1019,8 +1051,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)])
...@@ -1065,6 +1100,10 @@ class TestOrderedDict(unittest.TestCase): ...@@ -1065,6 +1100,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))
......
...@@ -13,6 +13,9 @@ Core and Builtins ...@@ -13,6 +13,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.
What's New in Python 2.7.9 release candidate 1? What's New in Python 2.7.9 release candidate 1?
=============================================== ===============================================
......
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