Python nightmares: Implicit this

Posted on Mon 11 May 2015 in articles

My first line of defense against such heresy is PEP 20 - The Zen of Python, which states that Explicit is better than implicit. Next comes the fact that Python class methods are in functions, bound to a class instance via descriptor protocol. If this sounds complex, I strongly encourage you to check the great Descriptor HowTo Guide from the official documentation. In general, passing function arguments through magic variables is awful (think of Perl with its $)! Why pass an object through a magical this?

Finally, it is possible to hide self and pass this implicitly. The easiest way is to do it by decorating a method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# NOTE: all code examples are done in Python 3

def add_this(f):
    def wrapped(self, *args, **kwargs):
        f.__globals__['this'] = self
        return f(*args, **kwargs)
    return wrapped

class C:
    name = 'Alex'

    @add_this
    def say(phrase):
        print("{} says: {}".format(this.name, phrase))

c = C()
c.say('Can you believe it? There is no `self` here!')

Output:

1
Alex says: Can you believe it? There is no `self` here!

As you can see, there is no self argument in say() method, but there is an implicit this! What happens in add_this() decorator is that we modify function's __globals__ dictionary, adding this variable with value of self to the scope. Recall that __globals__ is

A reference to the dictionary that holds the function’s global variables — the global namespace of the module in which the function was defined [1].

Thus, modifying it, we also modify the global scope of the current module. This is certainly not the way to go, but is enough for simple demonstration.

If that is not crazy enough, let's write a metaclass which takes care of decorating methods automatically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import types

class AddThisMeta(type):
    def __new__(cls, name, bases, classdict):
        new_classdict = {
            key: add_this(val) if isinstance(val, types.FunctionType) else val
            for key, val in classdict.items()
        }
        new_class = type.__new__(cls, name, bases, new_classdict)
        return new_class

class D(metaclass=AddThisMeta):
    name = 'Daniel'

    def say(phrase):
        print("{} says: {}".format(this.name, phrase))

    def run():
        print("{} runs away :)".format(this.name))

d = D()
d.say('And now, there is only AddThisMeta!')
d.run()

Output:

1
2
Daniel says: And now, there is only AddThisMeta!
Daniel runs away :)

Here, the metaclass does the same job we did above: it wraps all the methods which have a plain function type in class dictionary via add_this() decorator.

As you can see, it is not hard at all to introduce an implicit this in your code. But please, for all the good we have in Python, don't even think about doing it!.