I have created a custom class, and I want to use the ** operator on a instance for passing it to a function. I have already defined __getitem__ and __iter__, but when I try f(**my_object), I'm getting
`TypeError: argument must be a mapping, not 'MyClass'`
What are the minimum required methods so that the custom class qualifies as a mapping?
** is not an operator, it is part of the call syntax:
If the syntax
**expressionappears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments.
So if your class implements the Mapping methods, then you should be good to go. You'll need more than just __getitem__ and __iter__ here.
A Mapping is a Collection, so must define at least __getitem__, __iter__, and __len__; in addition most of __contains__, keys, items, values, get, __eq__, and __ne__ would be expected. If your custom class directly inherits from collections.abc.Mapping, you only need to implement the first three.
Demo:
>>> from collections.abc import Mapping
>>> class DemoMapping(Mapping):
... def __init__(self, a=None, b=None, c=None):
... self.a, self.b, self.c = a, b, c
... def __len__(self): return 3
... def __getitem__(self, name): return vars(self)[name]
... def __iter__(self): return iter('abc')
...
>>> def foo(a, b, c):
... print(a, b, c)
...
>>> foo(**DemoMapping(42, 'spam', 'eggs'))
42 spam eggs
If you run this under a debugger, you'll see that Python calls the .keys() method, which returns a dictionary view, which then delegates to the custom class __iter__ method when the view is iterated over. The values are then retrieved with a series of __getitem__ calls. So for your specific case, what was missing was the .keys() method.
In addition, note that Python may enforce that the keys are strings!
>>> class Numeric(Mapping):
... def __getitem__(self, name): return {1: 42, 7: 'spam', 11: 'eggs'}[name]
... def __len__(self): return 3
... def __iter__(self): return iter((1, 7, 11))
...
>>> dict(Numeric())
{1: 42, 7: 'spam', 11: 'eggs'}
>>> def foo(**kwargs): print(kwargs)
...
>>> foo(**Numeric())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
Emulating container types
The first set of methods is used [...] to emulate a mapping...
It is also recommended that mappings provide the methods
keys(),values(),items(),get(),clear(),setdefault(),pop(),popitem(),copy(), andupdate()behaving similar to those for Python’s standard dictionary objects.It is recommended that [...] mappings [...] implement the
__contains__()method to allow efficient use of the in operator...It is further recommended that [...] mappings [...] implement the
__iter__()method to allow efficient iteration through the container
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With