Dlaczego potrzebujesz jawnie argumentu „self” w metodzie Pythona?

202

Podczas definiowania metody w klasie w Pythonie wygląda to mniej więcej tak:

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

Ale w niektórych innych językach, takich jak C #, istnieje odwołanie do obiektu, z którym metoda jest powiązana, za pomocą słowa kluczowego „this” bez deklarowania go jako argumentu w prototypie metody.

Czy była to celowa decyzja dotycząca projektu języka w Pythonie, czy też są jakieś szczegóły implementacji, które wymagają podania „siebie” jako argumentu?

Tylko czytać
źródło
15
Założę się, że chciałbyś również wiedzieć, dlaczego musisz jawnie pisać, selfaby uzyskać dostęp do członków - stackoverflow.com/questions/910020/…
Piotr Dobrogost
1
Ale wygląda to trochę na kociołek
Raghuveer
Trochę zagmatwane, ale warte zrozumienia stackoverflow.com/a/31367197/1815624
CrandellWS

Odpowiedzi:

94

Lubię cytować zen Pythona Petersa. „Wyraźne jest lepsze niż ukryte”.

W Javie i C ++ this.można wydedukować „ ”, chyba że masz nazwy zmiennych, które uniemożliwiają wywnioskowanie. Więc czasami tego potrzebujesz, a czasami nie.

Python decyduje się, aby takie rzeczy były jawne, a nie oparte na regułach.

Ponadto, ponieważ nic nie jest implikowane ani zakładane, części implementacji są ujawniane. self.__class__, self.__dict__I innych „wewnętrzne” struktury dostępne są w oczywisty sposób.

S.Lott
źródło
55
Chociaż byłoby miło mieć mniej tajemniczy komunikat o błędzie, gdy go zapomnisz.
Martin Beckett
10
Jednak gdy wywołujesz metodę, nie musisz przekazywać zmiennej obiektowej, czy nie łamie to zasady jawności? Jeśli chcesz zachować ten zen, musi to być coś takiego: obiekt.metoda (obiekt, param1, param2). Wygląda jakoś niespójnie ...
Vedmant
12
„wyraźne jest lepsze niż niejawne” - czy „styl” Pythona nie jest zbudowany wokół rzeczy, które są niejawne? np. niejawne typy danych, niejawne granice funkcji (brak {}), niejawny zakres zmiennych ... jeśli zmienne globalne w modułach są dostępne w funkcjach ... dlaczego nie miałoby być zastosowane do klas? Czy uproszczona reguła nie brzmiałaby po prostu: „wszystko, co zadeklarowano na wyższym poziomie, jest dostępne na niższym poziomie”, co określa wcięcie?
Simon
16
„Wyraźne jest lepsze niż ukryte” Wykryto bzdury
Vahid Amiri
13
spójrzmy prawdzie w oczy, po prostu jest źle. Nie ma na to usprawiedliwienia. To tylko brzydka relikwia, ale jest w porządku.
Toskan
65

Ma to na celu zminimalizowanie różnicy między metodami i funkcjami. Umożliwia łatwe generowanie metod w metaklasach lub dodawanie metod w czasie wykonywania do wcześniej istniejących klas.

na przykład

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

To także (o ile wiem) ułatwia implementację środowiska uruchomieniowego Pythona.

Ryan
źródło
11
+1 za To, aby zminimalizować różnicę między metodami i funkcjami. Ta odpowiedź powinna zostać zaakceptowana
użytkownik
To również leży u podstaw bardzo powiązanych wyjaśnień Guido.
Marcin
1
Pokazuje to również, że w Pythonie, gdy wykonujesz c.bar () najpierw sprawdza instancję pod kątem atrybutów, a następnie sprawdza atrybuty klasy . Możesz więc „dołączyć” dane lub funkcję (obiekty) do klasy w dowolnym momencie i oczekiwać, że uzyskasz dostęp w jej instancji (tj. Dir (instancja) tak będzie). Nie tylko wtedy, gdy „utworzyłeś” instancję c. Jest bardzo dynamiczny.
Nishant
10
Naprawdę tego nie kupuję. Nawet w przypadkach, gdy potrzebujesz klasy nadrzędnej, możesz ją wywnioskować po wykonaniu. A równoważność między metodami instancji a przekazanymi instancjami funkcji klasowych jest głupia; Ruby radzi sobie bez nich dobrze.
zachaysan
2
JavaScript umożliwia dodawanie metod do obiektu w czasie wykonywania i nie wymaga selfdeklaracji funkcji (pamiętaj, być może jest to wyrzucanie kamieni ze szklanego domu, ponieważ JavaScript ma dość skomplikowaną thissemantykę wiązania)
Jonathan Benn
55

Proponuję przeczytać blog Guido van Rossuma na ten temat - Dlaczego jawne ja musi zostać .

Kiedy dekorowana jest definicja metody, nie wiemy, czy automatycznie nadawać jej parametr „self”, czy też nie: dekorator mógłby przekształcić funkcję w metodę statyczną (która nie ma „self”), czy metodę klasową (która ma zabawny rodzaj self, które odwołuje się do klasy zamiast instancji) lub może zrobić coś zupełnie innego (napisanie dekoratora, który implementuje '@classmethod' lub '@staticmethod' w czystym Pythonie, jest trywialne). Nie ma sposobu, nie wiedząc, co robi dekorator, czy obdarzyć definiowaną metodę niejawnym argumentem „ja”, czy nie.

Odrzucam hacki typu „@classmethod” i „@staticmethod”.

bhadra
źródło
16

Python nie zmusza cię do używania "self". Możesz nadać mu dowolną nazwę. Musisz tylko pamiętać, że pierwszy argument w nagłówku definicji metody to odwołanie do obiektu.

Victor Noagbodji
źródło
Zgodnie z konwencją powinno to być jednak
``
1
Zmusza do umieszczenia siebie jako pierwszego parametru w każdej metodzie, tylko dodatkowego tekstu, który nie ma większego sensu jak dla mnie. Inne języki dobrze sobie z tym radzą.
Vedmant
czy mam rację ? zawsze pierwszym parametrem jest odniesienie do obiektu.
Mohammad Mahdi KouchakYazdi
@MMKY Nie, na przykład z @staticmethodnim nie ma.
Mark
1
„Musisz tylko pamiętać, że pierwszy argument w definicji metody…” Eksperymentowałem ze zmianą słowa „self” na „kwyjibo” i nadal działało. Jak często się tłumaczy, to nie słowo „ja” jest ważne, ale pozycja tego, co zajmuje tę przestrzeń (?)
RBV,
7

Pozwala również na to: (w skrócie, wywołanie Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)zwróci 12, ale zrobi to w najbardziej szalony sposób.

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

Oczywiście trudniej to sobie wyobrazić w językach takich jak Java i C #. Wyraźnie określając odniesienie do siebie, możesz swobodnie odwoływać się do dowolnego obiektu za pomocą tego odniesienia. Ponadto taki sposób grania z klasami w czasie wykonywania jest trudniejszy do wykonania w bardziej statycznych językach - niekoniecznie jest to dobre lub złe. Chodzi tylko o to, że jawne ja pozwala istnieć całym tym szaleństwu.

Co więcej, wyobraź sobie: Chcielibyśmy dostosować zachowanie metod (do profilowania lub jakiejś szalonej czarnej magii). Może to nas skłonić do myślenia: co by było, gdybyśmy mieli klasę, Methodktórej zachowanie moglibyśmy zmienić lub kontrolować?

Cóż, oto jest:

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()

A teraz: InnocentClass().magic_method()będzie działać zgodnie z oczekiwaniami. Metoda zostanie powiązana z innocent_selfparametrem to InnocentClassi z magic_selfinstancją MagicMethod. Dziwne co? To tak, jakby 2 słowa kluczowe this1i this2w językach takich jak Java i C #. Taka magia pozwala frameworkom robić rzeczy, które w innym przypadku byłyby znacznie bardziej szczegółowe.

Ponownie, nie chcę komentować etyki tych rzeczy. Chciałem tylko pokazać rzeczy, które byłyby trudniejsze do zrobienia bez wyraźnego odniesienia do siebie.

vlad-ardelean
źródło
4
Kiedy rozważę twój pierwszy przykład, mogę zrobić to samo w Javie: klasa wewnętrzna musi wywołać, OuterClass.thisaby uzyskać „self” z klasy zewnętrznej, ale nadal możesz używać jej thisjako odniesienia do siebie; bardzo podobne do tego, co robisz tutaj w Pythonie. Nie było mi trudniej to sobie wyobrazić. Może zależy to od biegłości w danym języku?
klaar
Ale czy nadal możesz odwoływać się do jakichkolwiek zakresów, gdy jesteś wewnątrz metody klasy anonimowej, która jest zdefiniowana w klasie anonimowej, która jest zdefiniowana w anonimowej implementacji interfejsu Something, która z kolei jest zdefiniowana w kolejnej anonimowej implementacji Something? W Pythonie możesz oczywiście odwołać się do dowolnego z zakresów.
vlad-ardelean
Masz rację, w Javie możesz odwoływać się do klasy zewnętrznej tylko przez wywołanie jej jawnej nazwy klasy i użycie jej jako prefiksu this. Niejawne odwołania są niemożliwe w Javie.
klaar
Zastanawiam się, czy to jednak zadziała: w każdym zakresie (każdej metodzie) ma zmienną lokalną, która odwołuje się do thiswyniku. Na przykład Object self1 = this;(użyj Object lub czegoś mniej ogólnego). Następnie, jeśli masz dostęp do zmiennej w wyższych zakresach, można mieć dostęp do self1, self2... selfn. Myślę, że powinny one zostać uznane za ostateczne lub coś w tym rodzaju, ale może zadziałać.
vlad-ardelean
3

Myślę, że prawdziwym powodem oprócz „Zen of Python” jest to, że funkcje są obywatelami pierwszej klasy w Pythonie.

Co zasadniczo czyni je przedmiotem. Teraz podstawową kwestią jest to, że jeśli twoje funkcje również są obiektami, w jaki sposób w paradygmacie zorientowanym obiektowo wysyłasz komunikaty do obiektów, gdy same komunikaty są obiektami?

Wygląda na problem z jajami kurzymi. Aby zmniejszyć ten paradoks, jedynym możliwym sposobem jest przekazanie kontekstu wykonania do metod lub wykrycie go. Ale ponieważ Python może mieć funkcje zagnieżdżone, byłoby to niemożliwe, ponieważ kontekst wykonania zmieniłby się dla funkcji wewnętrznych.

Oznacza to, że jedynym możliwym rozwiązaniem jest jawne przekazanie „siebie” (kontekstu wykonania).

Więc uważam, że jest to problem z implementacją Zen pojawił się znacznie później.

pankajdoharey
źródło
Cześć, Jestem nowy w Pythonie (z javy w tle) i nie do końca wypowiedziałeś to, co powiedziałeś "jak wysyłać wiadomości do Objects, gdy same wiadomości są obiektami". Dlaczego to jest problem, czy możesz to rozwinąć?
Qiulang
1
@Qiulang Aah, w programowaniu obiektowym wywoływanie metod na obiektach jest równoważne wysyłaniu komunikatów do obiektów z ładunkiem lub bez ładunku (parametry do funkcji). Metody wewnętrznie byłyby reprezentowane jako blok kodu skojarzony z klasą / obiektem i wykorzystują niejawne środowisko dostępne dla niego za pośrednictwem obiektu, dla którego są wywoływane. Ale jeśli twoje metody są obiektami, mogą istnieć niezależnie od przypisania ich do klasy / obiektu, co nasuwa pytanie, czy wywołasz tę metodę, z którym środowiskiem by ona działała?
pankajdoharey
Dlatego musi istnieć mechanizm zapewniający środowisko, self oznaczałoby obecne środowisko w momencie wykonania, ale można też zapewnić inne środowisko.
pankajdoharey
2

Myślę, że ma to związek z PEP 227:

Nazwy w zakresie klas nie są dostępne. Nazwy są rozwiązywane w najbardziej wewnętrznym obejmującym zakresie funkcji. Jeśli definicja klasy występuje w łańcuchu zagnieżdżonych zasięgów, proces rozstrzygania pomija definicje klas. Ta reguła zapobiega dziwnym interakcjom między atrybutami klas a dostępem do zmiennych lokalnych. Jeśli operacja wiązania nazwy występuje w definicji klasy, tworzy atrybut na wynikowym obiekcie klasy. Aby uzyskać dostęp do tej zmiennej w metodzie lub w funkcji zagnieżdżonej w metodzie, należy użyć odwołania do atrybutu, poprzez self lub za pośrednictwem nazwy klasy.

daole
źródło
1

Jak wyjaśniono w self w Pythonie, Demystified

coś w rodzaju obj.meth (args) staje się Class.meth (obj, args). Proces wywołujący jest automatyczny, podczas gdy proces odbierający nie jest (jest to jawne). Z tego powodu pierwszym parametrem funkcji w klasie musi być sam obiekt.

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

Modły:

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

w tym () definiuje trzy parametry, ale właśnie przekazaliśmy dwa (6 i 8). Podobnie distance () wymaga podania jednego, ale nie przekazano żadnych argumentów.

Dlaczego Python nie narzeka na niezgodność numeru tego argumentu ?

Generalnie, gdy wywołujemy metodę z pewnymi argumentami, wywoływana jest odpowiednia funkcja klasy poprzez umieszczenie obiektu metody przed pierwszym argumentem. Zatem wszystko, jak obj.meth (args), staje się Class.meth (obj, args). Proces wywołujący jest automatyczny, a proces odbierający nie (jest to jawne).

Z tego powodu pierwszym parametrem funkcji w klasie musi być sam obiekt. Zapisywanie tego parametru jako self jest jedynie konwencją . Nie jest to słowo kluczowe i nie ma specjalnego znaczenia w Pythonie. Moglibyśmy użyć innych nazw (takich jak ta), ale zdecydowanie odradzam. Większość programistów spotyka się z dezaprobatą używania nazw innych niż self i pogarsza czytelność kodu („Liczy się czytelność”).
...
W pierwszym przykładzie self.x jest atrybutem instancji, podczas gdy x jest zmienną lokalną. Nie są takie same i znajdują się w różnych przestrzeniach nazw.

Self Is Here to Stay

Wiele osób zaproponowało uczynienie siebie słowem kluczowym w Pythonie, na przykład w C ++ i Javie. Pozwoliłoby to wyeliminować zbędne użycie jawnego self z formalnej listy parametrów w metodach. Chociaż ten pomysł wydaje się obiecujący, to się nie wydarzy. Przynajmniej nie w najbliższej przyszłości. Głównym powodem jest kompatybilność wsteczna . Oto blog od samego twórcy Pythona wyjaśniający, dlaczego jawne ja musi zostać.

pon
źródło
0

Parametr „self” zachowuje bieżący obiekt wywołujący.

class class_name:
    class_variable
    def method_name(self,arg):
        self.var=arg 
obj=class_name()
obj.method_name()

tutaj argument self przechowuje obiekt obj. Stąd wyrażenie self.var oznacza obj.var

Sanmitha Sadhishkumar
źródło
-5

Jest też inna bardzo prosta odpowiedź: zgodnie z zenem Pythona , „wyraźne jest lepsze niż niejawne”.

Flávio Amieiro
źródło