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

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

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

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

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

Ответ 1

Мне нравится цитировать "Zen of Python" Peters. "Явный лучше, чем неявный".

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

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

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

Ответ 2

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

например.

>>> 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.

Ответ 3

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

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

Я отклоняю хаки как специальные "@classmethod" и "@staticmethod".

Ответ 4

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

Ответ 5

Также позволяет это сделать: (короче говоря, вызов 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 и С#. Сделав самостоятельную ссылку явным, вы можете обращаться к любому объекту с помощью этой самостоятельной ссылки. Кроме того, такой способ игры с классами во время выполнения сложнее делать на более статических языках - а не на том, что это обязательно хорошо или плохо. Это просто, что явное "я" позволяет всему этому сумасшествию существовать.

Кроме того, представьте это: мы хотели бы настроить поведение методов (для профилирования или какой-то сумасшедшей черной магии). Это может заставить нас думать: что, если бы у нас был класс 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. Странно, да? Это похоже на наличие 2 ключевых слов this1 и this2 в таких языках, как Java и С#. Магия, подобная этому, позволяет фреймворкам делать вещи, которые в противном случае были бы намного более подробными.

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Итак, я считаю, что это проблема реализации, которую Дзэн пришел намного позже.

Ответ 8

Как объяснил в себе в Python, Демистифицированный

что-нибудь вроде 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). Вызывающий процесс является автоматическим, в то время как процесс получения не является (его явным).

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

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

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

Ответ 9

Существует еще один очень простой ответ: согласно zen python, "явный лучше, чем неявный".