http://habrahabr.ru/post/257887/
Обсуждение статьи "
Не совсем крутой Ruby" зашло достаточно далеко: недостатки и достоинства Ruby между делом перетекали в обсуждение недостатков и достоинств Python. Не сильно удивило то, что передача
self
в качестве первого аргумента метода класса, некоторым хабравчанам кажется лишней. Ну что ж, не хотите явного
self
, будет вам неявный
this
! Под катом, немного магии на чистом Python.
Но сначала, давайте всё-таки поговорим о том, почему
self
передаётся явным образом. Как мне кажется, причины на то две. Первая — это
The Zen of Python, в котором чёрным по белому написано:
Explicit is better than implicit (явное лучше неявного).
Это относится и к передачи данного объекта в метод явным образом, через
self
.
Вторая причина не менее важна — это дескрипторы. ООП в Python реализован на уровне функций, которые привязываются к объекту динамически посредством механизма дескрипторов (обязательно прочтите статью
Руководство к дескрипторам). Итак, вернёмся к функциям: многие ли из нас любят волшебные переменные, через которые могут передаваться аргументы функции? Это например
$
в Perl,
arguments
в JS,
func_get_args()
в PHP. В Python нет таких волшебных переменных, всё, что передаётся в функцию, передаётся явным образом (в т.ч. и через
*args
и
**kwargs
). Так почему же для методов, которые Python обрабатывает как обыкновенные функции, должно быть сделано исключение в виде неявной передачи
self
?
Однако, в качестве упражнения сделать это совсем несложно. Давайте начнём с простого декоратора:
# Все примеры на 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!')
На выходе:
Alex says: Can you believe it? There is no `self` here!
Как видите, декоратор
add_this
добавляет переменную
this
в область видимости функции, и присваивает ей значение
self
. Вспомните, что
__globals__
— это поле ссылающееся на словарь содержащий глобальные переменные функции, т.е. глобальное пространство имён модуля, в котором эта функция объявлена. Таким образом, вышенаписанный код — это грязнющий хак, добавляющий (и затирающий!) переменную
this
в глобальное пространство модуля. Всё это подойдёт для наших экспериментов, но упаси вас писать такое в настоящем коде!
Предвкушая комментарии аудитории о том, что так каждую функцию придётся обрамлять в декоратор, предлагаю взвалить эту задачу на плечи метакласса:
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()
На выходе:
Daniel says: And now, there is only AddThisMeta!
Daniel runs away :)
Метакласс проходит по всем полям класса и их значениям, выбирает подходящие по типу (важный момент: простая проверка на
callable()
не подойдёт, т.к. она также сработает для
classmethod
и
staticmethod
) и обрамляет эти функции декоратором
add_this
.
Как вы видите, добавить неявный
self
(или
this
) в методы классa совсем не сложно. Но прошу вас, ради всего хорошего, что есть в Python, никогда,
никогда,
никогда не делайте этого.