Mike
Mike

Reputation: 60751

Dynamically overriding __functions__ in Python

Let's say I want to override a function like __int__ in a Python class so that I may do something like this.

class A(object):
    def __init__(self):
        self.__int__ = lambda: 1

a = A()
print int(a)

I expect that it would output "1" here instead of produce this error message

TypeError: int() argument must be a string or a number, not 'A'

When __int__ instead becomes a method built into the class it works as expected. Why? (This problem exists with any of the double underscore functions also)

Upvotes: 4

Views: 257

Answers (2)

Lennart Regebro
Lennart Regebro

Reputation: 172229

If we for the moment ignore that you are asking about special methods, then your code would look like this:

class A(object):
    def __init__(self):
        self.a_fun = lambda: 1

Is written more clearly like this:

class A(object):
    def __init__(self):
        self._int = 1

    def a_fun(self):
        return self._int

The resulting code isn't exactly the same, but close enough for it to not make much of a difference. The only difference is that the _int name has to be looked up as an attribute.

But if we now change it back to be a special method, it looks like this:

class A(object):
    def __init__(self):
        self.__int__ = lambda: 1

vs:

class A(object):
    def __init__(self):
        self._int = 1

    def __int__(self):
        return self._int

And now there is a very big difference: The second variant works, the first one doesn't. This is because special methods is always looked up on the class, not the instance. This is by design.

So instead of trying to be clever, just write what is clear and readable. In Python that tends to work best. ;-)

Upvotes: 1

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

That appears to be one more bit of magic in the __ magic methods. Unlike other methods, they're not looked up on the class instances when called implicitly.

It's well documented that they don't get resolved with the __getattribute__ magic method (and it would be a nice paradox if they did, since __getattribute__ would have to call itself to resolve its own name). But not checking the instances surprises me.

It's discussed a bit here, under the header "Special Method Lookup": http://segfaulthunter.github.io/articles/biggestsurprise/

For instances of new-style classes, all special method lookup that is done implicitely is done in the class struct. Thus changing an instance's __str__ attribute does not effect the result of str(). Still, explicitely getting the attribute from the instance gives you the method in the instance.

I will be curious to see if anyone else can offer a more detailed explanation.

Upvotes: 3

Related Questions