Is there a way in Python to add agnostically to a collection?
Given the prevalence of duck typing I was surprised that the method to add to a list
is append(x)
but the method to add to a set
is add(x)
.
I'm writing a family of utility functions that need to build up collections and would ideally like them not to care what type is accumulating the result. It should at least work for list
and set
- and ideally for other targets, as long as they know what method to implement. Essentially, the duck type here is 'thing to which items can be added'.
In practice, these utility functions will either be passed the target object to add the results to, or - more commonly - a function that generates new instances of the target type when needed.
For example:
def collate(xs, n, f_make=lambda: list()):
if n < 1:
raise ValueError('n < 1')
col = f_make()
for x in xs:
if len(col) == n:
yield col
col = f_make()
col.append(x) # append() okay for list but not for set
yield col
>>> list(collate(range(6), 3))
[[0, 1, 2], [3, 4, 5]]
>>> list(collate(range(6), 4))
[[0, 1, 2, 3], [4, 5]]
>>> # desired result here: [{0, 1, 2, 3}, {4, 5}]
>>> list(collate(range(6), 4, f_make=lambda: set()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/paul/proj/mbrain/src/fossil/fn.py", line 42, in collate
col.append(x)
AttributeError: 'set' object has no attribute 'append'
Here collate()
is just a simple example. I expect there's already a way to achieve this 'collation' in Python. That's not the real question here.
I'm currently using Python 3.8.5.
Returning to this later I found a better solution using @functools.singledispatch
which is also user-extensible to additional types.
import functools
@functools.singledispatch
def append(xs, v):
raise ValueError('append() not supported for ' + str(type(xs)))
@append.register
def _(xs: MutableSequence, v):
xs.append(v)
@append.register
def _(xs: MutableSet, v):
xs.add(v)