Search code examples
pythonclassassignmagic-methods

Assigning vs. Defining Python Magic Methods


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.


Solution

  • 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.