Чому явно потрібно мати аргумент "self" у методі Python?


197

Визначаючи метод на класі в Python, він виглядає приблизно так:

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

Але в деяких інших мовах, таких як C #, ви маєте посилання на об'єкт, до якого метод пов'язаний з ключовим словом "це", не оголошуючи його як аргумент у прототипі методу.

Чи було це навмисне мовне дизайнерське рішення в Python чи є якісь деталі реалізації, які потребують передачі "Я" в якості аргументу?


15
Надіюсь, вам також буде цікаво знати, чому вам потрібно чітко писати selfдоступ до членів - stackoverflow.com/questions/910020/…
— Piotr Dobrogost

1
Але це виглядає
— якось

Трохи заплутано, але варто зрозуміти stackoverflow.com/a/31367197/1815624
— CrandellWS

Відповіді:


91

Мені подобається цитувати Пітерсона Дзен Пітона. "Явне краще, ніж неявне."

У Java та C ++ ' this.' можна вивести, за винятком випадків, коли у вас є імена змінних, які унеможливлюють виведення. Тож вам іноді це потрібно, а іноді - ні.

Python вирішує зробити такі речі явними, а не спиратися на правило.

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


53
Хоча було б непогано мати менш криптові повідомлення про помилку, коли ви це забудете.
— Мартін Бекетт

9
Однак, коли ви викликаєте метод, вам не потрібно передавати змінну об'єкта, чи не порушує це правило явності? Якщо зберегти цей дзен, він повинен бути чимось на зразок: object.method (об’єкт, param1, param2). Виглядає якось непослідовно ...
— Ведмант

10
"явне краще, ніж неявне" - Чи не "стиль" Python, побудований навколо речей, неявний? наприклад, неявні типи даних, неявні межі функцій (немає {} s), неявна область змінної ... якщо глобальні змінні модулі доступні у функціях ... чому не слід застосовувати таку саму логіку / міркування до класів? Чи не буде спрощеним правилом просто "все, що оголошено на більш високому рівні, доступне на нижчому рівні", як це визначено відступом?
— Саймон

13
"Явне краще, ніж неявне" Виявлено дурниці
— Вахід Амірі

10
визнаймо, це просто погано. Виправдання для цього немає. Це просто потворна реліквія, але це нормально.
— Тоскан

63

Це звести до мінімуму різницю між методами та функціями. Це дозволяє легко генерувати методи в метакласах або додавати методи під час виконання до вже існуючих класів.

напр

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

Це також (наскільки я знаю) полегшує реалізацію виконання пітона.


10
+1 для Це мінімізувати різницю між методами та функціями. Цю відповідь слід прийняти
— користувач

Це також є коренем багатозв'язаного пояснення гідо.
— Marcin

1
Це також показує, що в Python, коли ви c.bar () спочатку перевіряє екземпляр на атрибути, потім перевіряє атрибути класу . Таким чином, ви можете будь-коли "приєднати" дані або функцію (об'єкти) до класу і очікувати доступу в його екземплярі (тобто dir (екземпляр) буде як це). Не лише тоді, коли ви "створили" екземпляр c. Це дуже динамічно.
— Нішант

10
Я не дуже купую це. Навіть у тих випадках, коли вам потрібен батьківський клас, ви все одно можете зробити висновок про його виконання. І еквівалентність методів екземплярів та функцій класу, переданих екземплярам, ​​нерозумна; Рубі без них справляється чудово.
— зачаян

2
JavaScript дозволяє додавати методи до об’єкта під час виконання, і це не вимагає selfу оголошенні функції (пам’ятайте, можливо, це викидання каменів із скляного будинку, оскільки у JavaScript є досить складна thisсемантика прив’язки)
— Джонатан Бенн

55

Я пропоную прочитати блог Гідо ван Россума на цю тему - Чому явне самоврядування повинно залишатися .

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

Я відкидаю хаки, такі як "@classmethod" та "@staticmethod".


16

Python не змушує вас використовувати "self". Ви можете дати йому будь-яке ім'я. Вам просто потрібно пам’ятати, що перший аргумент у заголовку визначення методу - це посилання на об’єкт.


За умовою, однак, це має бути "self" для випадків або "cls", де задіяні типи (мммм метакласи)
— pobk

1
Це змушує ставити себе як перший параметр у кожному методі, просто зайвий текст, який не має великого сенсу, як для мене. Інші мови працюють із цим просто чудово.
— Ведманта

маю рацію ? завжди перший параметр - це посилання на об'єкт.
— Мохаммед Махді КучакЯзді

@MMKY Ні, наприклад з @staticmethodним немає.
— Марк

1
"Ви просто повинні пам'ятати, що перший аргумент у визначенні методу ..." Я експериментував зі зміною слова "self" на "kwyjibo", і він все-таки спрацював. Тому, як це часто пояснюється, важливо не слово "Я", а положення того, що займає цей простір (?)
— RBV

7

Також дозволяє це зробити: (коротше кажучи, виклик 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 #. Магія, як це, дозволяє рамкам робити речі, які інакше були б набагато більш багатослівними.

Знову ж таки, я не хочу коментувати етику цього матеріалу. Мені просто хотілося показати речі, які було б важче зробити без явного посилання на себе.


4
Коли я розглядаю ваш перший приклад, я можу зробити те ж саме в Java: внутрішній клас повинен викликати, OuterClass.thisщоб отримати "Я" від зовнішнього класу, але ви все одно можете використовувати його thisяк посилання на себе; дуже схожий на те, що ви робите тут, у Python. Для мене це було не важче уявити. Може, це залежить від володіння мовою, про яку йде мова?
— klaar

Але чи можете ви все-таки звернутися до будь-яких областей, коли ви знаходитесь в методі анонімного класу, який визначений всередині анонімного класу, який визначений всередині анонімної реалізації інтерфейсу Something, який, у свою чергу, визначається всередині ще однієї анонімної реалізації Something? Зрозуміло, що в python ви можете звернутися до будь-якої області застосування.
— vlad-ardelean

Ви маєте рацію, у Java ви можете звернутися до зовнішнього класу лише зателефонувавши до його явного імені класу та використовувати його для префікса this. Неявні посилання на Яві неможливі.
— klaar

Цікаво, чи це не спрацювало б: У кожному діапазоні (кожному методі) є локальна змінна, яка посилається на thisрезультат. Наприклад Object self1 = this;(або використовувати Object, або щось менш загальне). Тоді, якщо у вас є доступ до змінної в вищих областях, ви могли б мати доступ до self1, self2... selfn. Я думаю, що їх слід оголосити остаточними чи що-небудь, але це може спрацювати.
— vlad-ardelean

3

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

Що по суті робить їх Об'єктом. Тепер основоположним питанням є те, якщо ваші функції також об'єктні, то в об'єктно-орієнтованій парадигмі, як би ви надсилали повідомлення об’єктам, коли самі повідомлення є об'єктами?

Схоже на проблему з курячим яйцем, щоб зменшити цей парадокс, єдиний можливий спосіб - це передати контекст виконання методам або виявити його. Але оскільки python може мати вкладені функції, зробити це було б неможливо, оскільки контекст виконання змінився б для внутрішніх функцій.

Це означає, що єдино можливим рішенням є явне проходження 'себе' (контекст виконання).

Тож я вважаю, що це проблема впровадження, в яку з'явився Дзен значно пізніше.


Привіт, я новачок у python (з фону java), і я не дуже передавав те, що ви сказали "як би ви надсилали повідомлення об'єктам, коли самі повідомлення є об'єктами". Чому це проблема, ви можете розробити?
— Qiulang

1
@Qiulang Aah, в об'єктно-орієнтованому програмуванні методи виклику на об'єктах еквівалентні розсилці повідомлень до Об'єктів з корисним навантаженням або без нього (параметри вашої функції). Методи внутрішньо будуть представлені як блок коду, асоційований з класом / об'єктом, і використовує неявну середу, доступну йому через об'єкт, проти якого викликається. Але якщо ваші методи - це об'єкти, вони можуть існувати незалежно від асоціації з класом / об'єктом, що задає питання, якщо ви посилаєтесь на цей метод, до якого середовища воно би запускалося?
— pankajdoharey

Таким чином, повинен існувати механізм забезпечення середовища, а саме означатиме поточне середовище на місці виконання, але ви також можете надати інше середовище.
— pankajdoharey

2

Я думаю, що це стосується PEP 227:

Назви в межах класу недоступні. Імена розв’язуються в найпотужнішій області застосування функції. Якщо визначення класу відбувається в ланцюжку вкладених областей, процес роздільної здатності пропускає визначення класів. Це правило запобігає непарній взаємодії між атрибутами класу та локальним змінним доступом. Якщо операція зв’язування імені відбувається у визначенні класу, вона створює атрибут на результуючому об'єкті класу. Для доступу до цієї змінної у методі або у функції, вкладеній у метод, слід використовувати посилання на атрибут, або через self, або через ім'я класу.


1

Як пояснено в самоврядування в Python, Demystified

все на зразок 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). Аналогічно відстань () вимагає передавання одного, але нульового аргументу.

Чому Python не скаржиться на невідповідність цього аргументу ?

Як правило, коли ми викликаємо метод з деякими аргументами, відповідна функція класу викликається шляхом розміщення об'єкта методу перед першим аргументом. Отже, що-небудь на зразок obj.meth (args) стає Class.meth (obj, args). Процес виклику є автоматичним, поки процес прийому не є (явний).

З цієї причини першим параметром функції в класі повинен бути сам об’єкт. Запис цього параметра як самостійна - це лише умова . Це не ключове слово і не має особливого значення в Python. Ми можемо використовувати інші назви (як це), але я настійно пропоную вам цього не робити. Використання імен, окрім self, спокушається більшістю розробників і погіршує читабельність коду ("Читання читання").
...
У першому прикладі self.x є атрибутом екземпляра, тоді як x - локальна змінна. Вони не однакові і лежать у різних просторах імен.

Сам тут залишився

Багато хто запропонував самостійно зробити ключове слово в Python, наприклад, у C ++ та Java. Це дозволило б усунути зайве використання явного "Я" зі списку формальних параметрів у методах. Хоча ця ідея здається перспективною, вона не відбудеться. Принаймні, не найближчим часом. Основна причина - відстала сумісність . Ось блог від самого творця Python, який пояснює, чому явне «я» має залишатися.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.