Clarify that Set._from_iterable is not required to be a classmethod. (GH-23272) (GH-23450)
This commit is contained in:
parent
3b5b1c82a3
commit
89d74d0acd
|
@ -291,7 +291,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin:
|
|||
:meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set.
|
||||
If the :class:`Set` mixin is being used in a class with a different
|
||||
constructor signature, you will need to override :meth:`_from_iterable`
|
||||
with a classmethod that can construct new instances from
|
||||
with a classmethod or regular method that can construct new instances from
|
||||
an iterable argument.
|
||||
|
||||
(2)
|
||||
|
|
|
@ -1558,6 +1558,62 @@ class TestCollectionABCs(ABCTestCase):
|
|||
# coerce both to a real set then check equality
|
||||
self.assertSetEqual(set(s1), set(s2))
|
||||
|
||||
def test_Set_from_iterable(self):
|
||||
"""Verify _from_iterable overriden to an instance method works."""
|
||||
class SetUsingInstanceFromIterable(MutableSet):
|
||||
def __init__(self, values, created_by):
|
||||
if not created_by:
|
||||
raise ValueError(f'created_by must be specified')
|
||||
self.created_by = created_by
|
||||
self._values = set(values)
|
||||
|
||||
def _from_iterable(self, values):
|
||||
return type(self)(values, 'from_iterable')
|
||||
|
||||
def __contains__(self, value):
|
||||
return value in self._values
|
||||
|
||||
def __iter__(self):
|
||||
yield from self._values
|
||||
|
||||
def __len__(self):
|
||||
return len(self._values)
|
||||
|
||||
def add(self, value):
|
||||
self._values.add(value)
|
||||
|
||||
def discard(self, value):
|
||||
self._values.discard(value)
|
||||
|
||||
impl = SetUsingInstanceFromIterable([1, 2, 3], 'test')
|
||||
|
||||
actual = impl - {1}
|
||||
self.assertIsInstance(actual, SetUsingInstanceFromIterable)
|
||||
self.assertEqual('from_iterable', actual.created_by)
|
||||
self.assertEqual({2, 3}, actual)
|
||||
|
||||
actual = impl | {4}
|
||||
self.assertIsInstance(actual, SetUsingInstanceFromIterable)
|
||||
self.assertEqual('from_iterable', actual.created_by)
|
||||
self.assertEqual({1, 2, 3, 4}, actual)
|
||||
|
||||
actual = impl & {2}
|
||||
self.assertIsInstance(actual, SetUsingInstanceFromIterable)
|
||||
self.assertEqual('from_iterable', actual.created_by)
|
||||
self.assertEqual({2}, actual)
|
||||
|
||||
actual = impl ^ {3, 4}
|
||||
self.assertIsInstance(actual, SetUsingInstanceFromIterable)
|
||||
self.assertEqual('from_iterable', actual.created_by)
|
||||
self.assertEqual({1, 2, 4}, actual)
|
||||
|
||||
# NOTE: ixor'ing with a list is important here: internally, __ixor__
|
||||
# only calls _from_iterable if the other value isn't already a Set.
|
||||
impl ^= [3, 4]
|
||||
self.assertIsInstance(impl, SetUsingInstanceFromIterable)
|
||||
self.assertEqual('test', impl.created_by)
|
||||
self.assertEqual({1, 2, 4}, impl)
|
||||
|
||||
def test_Set_interoperability_with_real_sets(self):
|
||||
# Issue: 8743
|
||||
class ListSet(Set):
|
||||
|
|
Loading…
Reference in New Issue