📜 ⬆️ ⬇️

Python nightmares: the implicit `this`

Discussion of the article " Not quite cool Ruby " has gone far enough: the disadvantages and merits of Ruby between business flowed into the discussion of the shortcomings and merits of Python. Not very surprised by the fact that passing self as the first argument of a class method seems to some habravchanam to be superfluous. Well, if you don't want an explicit self , you will have an implicit this ! Under the cut, a little magic on pure Python.

But first, let's still talk about why self passed explicitly. It seems to me that there are two reasons for this. The first is The Zen of Python , in which it is written in black and white:
Explicit is better than implicit (explicit better than implicit).
This also applies to the transfer of this object to the method explicitly, through self .

The second reason is no less important - these are handles. OOP in Python is implemented at the level of functions that are attached to an object dynamically through the mechanism of descriptors (be sure to read the Handbook to Descriptors article). So, back to the functions: How many of us like magic variables through which function arguments can be passed? These are for example $ in Perl, arguments in JS, func_get_args() in PHP. In Python, there are no such magic variables, everything that is passed to a function is passed explicitly (including via *args and **kwargs ). So why, for methods that Python processes as ordinary functions, should an exception be made in the form of an implicit self transfer?

However, as an exercise, it is not difficult to do this. Let's start with a simple decorator:
')
 #    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!') 

At the exit:

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

As you can see, the decorator add_this adds the variable this to the scope of the function, and assigns it the value self . Recall that __globals__ is a field referencing a dictionary containing global function variables, i.e. The global module namespace in which this function is declared. Thus, the above code is a dirty hack that adds (and overwrites!) The variable this to the global module space. All this is suitable for our experiments, but save you from writing this in real code!

Anticipating the comments from the audience that you will have to frame each function in the decorator, I propose to take this task on the shoulders of the metaclass:

 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() 

At the exit:

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

The metaclass passes through all the fields of the class and their values, selects the appropriate type (important point: a simple check on callable() will not work, because it will also work for the classmethod and staticmethod ) and frames these functions with the add_this decorator.

As you can see, adding implicit self (or this ) to class methods is not at all difficult. But please, for the good of everything in Python, never, never , never do it.

Source: https://habr.com/ru/post/257887/


All Articles