it-swarm-ru.tech

Зачем вам явно нужен аргумент "self" в методе Python?

При определении метода для класса в Python он выглядит примерно так:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Но в некоторых других языках, таких как C #, у вас есть ссылка на объект, к которому привязан метод, с ключевым словом "this" без объявления его в качестве аргумента в прототипе метода.

Было ли это преднамеренным решением о разработке языка в Python или есть какие-то детали реализации, которые требуют передачи "self" в качестве аргумента?

183
Readonly

Мне нравится цитировать Питерса Zen of Python. «Явное лучше, чем неявное».

В Java и C++ 'this.' может быть выведен, за исключением случаев, когда у вас есть имена переменных, которые делают невозможным вывод. Так что иногда это нужно, а иногда нет.

Python решает сделать такие вещи явными, а не основанными на правиле. 

Кроме того, поскольку ничего не подразумевается или не предполагается, части реализации раскрыты. self.__class__, self.__dict__ и другие «внутренние» структуры доступны очевидным образом.

88
S.Lott

Это минимизировать разницу между методами и функциями. Это позволяет вам легко генерировать методы в метаклассах или добавлять методы во время выполнения к уже существующим классам.

например.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Это также (насколько я знаю) облегчает реализацию среды исполнения Python.

55
Ryan

Я предлагаю прочитать блог Гвидо ван Россума на эту тему - Почему явное Я должно остаться .

Когда определение метода оформлено, мы не знаем, будет ли ему автоматически задан параметр «self» или нет: декоратор может превратить функцию в статический метод (у которого нет «self») или метод класса (который имеет забавный вид self, который ссылается на класс, а не на экземпляр), или он может сделать что-то совершенно другое (тривиально написать декоратор, который реализует '@classmethod' или '@staticmethod' в чистом Python). Нет никакого способа не знать, что делает декоратор, наделять ли определяемый метод неявным аргументом self или нет.

Я отвергаю такие хаки, как "@classmethod" и "@staticmethod" в специальном корпусе.

51
bhadra

Python не заставляет вас использовать «себя». Вы можете дать ему любое имя, какое захотите. Вы просто должны помнить, что первый аргумент в заголовке определения метода является ссылкой на объект.

16
Victor Noagbodji

Также позволяет вам сделать это: (короче говоря, вызов Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) вернет 12, но сделает это самым безумным способом.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Конечно, это сложнее представить в таких языках, как Java и C #. Сделав ссылку на себя явной, вы можете ссылаться на любой объект по этой ссылке. Кроме того, такой способ игры с классами во время выполнения сложнее сделать на более статичных языках - не обязательно это хорошо или плохо. Просто явное Я допускает существование всего этого безумия.

Кроме того, представьте себе следующее: мы хотели бы настроить поведение методов (для профилирования или какой-нибудь сумасшедшей черной магии). Это может привести нас к мысли: что если бы у нас был класс Method, поведение которого мы могли бы переопределить или контролировать?

Ну вот оно:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

И теперь: InnocentClass().magic_method() будет действовать как ожидалось. Метод будет связан с параметром innocent_self для InnocentClass, а с magic_self - для экземпляра MagicMethod. Странно, да? Это похоже на наличие двух ключевых слов this1 и this2 в таких языках, как Java и C #. Подобная магия позволяет фреймворкам делать вещи, которые в противном случае были бы гораздо более многословными.

Опять же, я не хочу комментировать этику этого материала. Я просто хотел показать вещи, которые было бы труднее сделать без явной ссылки на себя.

6
vlad-ardelean

Я думаю, что настоящая причина, помимо "Дзэн Питона", заключается в том, что Функции - это граждане первого класса в Питоне. 

Что по сути делает их объектом. Теперь фундаментальная проблема заключается в том, что если ваши функции также являются объектами, то в объектно-ориентированной парадигме как бы вы отправляли сообщения объектам, если сами сообщения являются объектами? 

Похоже на проблему куриного яйца, чтобы уменьшить этот парадокс, единственный возможный способ - передать контекст выполнения методам или обнаружить его. Но так как python может иметь вложенные функции, это невозможно сделать, так как контекст выполнения изменится для внутренних функций. 

Это означает, что единственно возможное решение - явно передать «я» (контекст исполнения).

Поэтому я считаю, что это проблема реализации, дзен появился намного позже.

2
pankajdoharey

Я думаю, что это связано с ПКП 227:

Имена в области видимости не доступны. Имена разрешены в самая внутренняя область действия функции. Если определение класса встречается в цепочка вложенных областей, процесс разрешения пропускает класс определения. Это правило предотвращает нечетные взаимодействия между классом атрибуты и доступ к локальной переменной. Если операция связывания имени происходит в определении класса, он создает атрибут в результате объект класса. Чтобы получить доступ к этой переменной в методе или в функции вложенный в метод, ссылка на атрибут должна использоваться либо через себя или через имя класса.

2
daole

Как объяснено в self в Python, Demysified

что-нибудь вроде obj.meth (args) становится Class.meth (obj, args). Вызывающий процесс является автоматическим, в то время как процесс получения не является (его явным). По этой причине первым параметром функции в классе должен быть сам объект. 

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from Origin"""
        return (self.x**2 + self.y**2) ** 0.5

Вызовы:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () определяет три параметра, но мы только что передали два (6 и 8). Точно так же для distance () требуется один, но передано ноль аргументов 

Почему Python не жалуется на это несоответствие номера аргумента?

Обычно, когда мы вызываем метод с некоторыми аргументами, вызывается соответствующая функция класса, помещая объект метода перед первым аргументом. Таким образом, что-нибудь вроде obj.meth (args) становится Class.meth (obj, args). Вызывающий процесс является автоматическим, в то время как процесс получения не является (его явным).

Это причина, по которой первым параметром функции в классе должен быть сам объект. Запись этого параметра как self - это просто соглашение. Это не ключевое слово и не имеет особого значения в Python. Мы могли бы использовать другие имена (как это), но я настоятельно рекомендую вам не делать этого. Использование имен, отличных от self, осуждается большинством разработчиков и ухудшает читабельность кода («Количество читабельности») . 
...
В первом примере self.x является атрибутом экземпляра, тогда как x является локальной переменной. Они не одинаковы и лежат в разных пространствах имен.

Я здесь, чтобы остаться

Многие предлагали сделать self ключевым словом в Python, например, в C++ и Java. Это исключило бы избыточное использование явного self из списка формальных параметров в методах. Хотя эта идея кажется многообещающей, этого не произойдет. По крайней мере, не в ближайшее время. Основная причина - обратная совместимость. Вот блог от самого создателя Python, объясняющий, почему явное «я» должно остаться.

0
mon