Чому явно потрібно мати аргумент "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.