Consider the following abhorrent class:
class MapInt:
__call__ = int
def __sub__(self, other):
return map(self, other)
__add__ = map
One can then call map(int, lst)
via MapInt() - lst
, i.e.
assert list(MapInt() - ['1','2','3'])) == [1,2,3] # passes
However, addition is not so cooperative:
assert list(MapInt() + ['1','2','3'])) == [1,2,3] # TypeError: map() must have at least two arguments.
This strangeness can be resolve by invoking the magic method directly:
assert list(MapInt.__add__(MapInt(), ['1','2','3'])) == [1,2,3] # passes
assert list(MapInt().__add__(MapInt(), ['1','2','3'])) == [1,2,3] # passes
So my question is, what gives? Assigning __add__
directly seems to "discard" the self
argument, but invoking the method itself or defining it in the standard way works fine.
The transformation of instance methods is described in the Python Data Model (emphasis mine):
Note that the transformation from function object to instance method object happens each time the attribute is retrieved from the instance [...] Also notice that this transformation only happens for user-defined functions; other callable objects (and all non-callable objects) are retrieved without transformation.
Since map
is a built-in, not a user-defined function, there is no transformation to an instance method, so the self
argument is not added.