Dlaczego potrzebujesz „siebie”. w Pythonie, aby odwoływać się do zmiennych instancji?

13

Programuję w wielu językach, takich jak Java, Ruby, Haskell i Python. Muszę przełączać się między wieloma językami dziennie z powodu różnych projektów, nad którymi pracuję. Problem polega na tym, że często zapominam pisać selfjako pierwszy parametr w definicjach funkcji w Pythonie tak samo, jak w przypadku wywoływania metod na tym samym obiekcie.

To powiedziawszy, jestem dość zaskoczony tym podejściem Pythona. Zasadniczo musimy pisać więcej, aby załatwić sprawę, w językach takich jak Java i Ruby rzeczy stają się proste dzięki automatycznemu odwoływaniu się do zmiennych w bieżącym obiekcie.

Moje pytanie brzmi: dlaczego jest to selfkonieczne? Czy jest to wyłącznie wybór stylu, czy jest powód, dla którego Python nie pozwala ci pominąć selfsposobu, w jaki Java i C ++ pozwalają ci pominąć this?

vivek
źródło
1
@gnat teraz tylko profesjonaliści i poważnie to dobre pytanie, które mnie denerwuje od kilku dni, proszę nie zabijaj go, oddając głos.
vivek
2
To pytanie zostało bardziej niż szczegółowo opisane na stronie stackoverflow.com/questions/2709821/… .
David Arno,
Rozumiem, że opiera się na stylu C polegającym na przekazaniu wskaźnika do struktury jako pierwszego argumentu.
Dannnno,
Pisanie @staticmethodprzed deklaracją metody pomija błąd (tylko dla informacji, a także niezalecany)
Yash

Odpowiedzi:

23

1) Dlaczego jest selfwymagany jako wyraźny parametr w podpisach metod?

Ponieważ metody są funkcjami i foo.bar(baz)są po prostu cukrem syntaktycznym bar(foo, baz). Klasy to tylko słowniki, w których niektóre wartości są funkcjami. (Konstruktory to również tylko funkcje, dlatego Python nie potrzebuje new). Można powiedzieć, że Python wyraźnie mówi, że obiekty są zbudowane z prostszych komponentów. Jest to zgodne z filozofią „jawne jest lepsze niż niejawne”.

Natomiast w Javie obiekty są naprawdę magiczne i nie można ich sprowadzić do prostszych elementów w języku. W Javie (przynajmniej do Java 8) funkcja jest zawsze metodą będącą własnością obiektu, a tej własności nie można zmienić z powodu statycznej natury języka. Dlatego nie ma dwuznaczności w thisodniesieniu do tego, co się odnosi, więc sensowne jest, aby to domyślnie zdefiniować.

JavaScript jest przykładem języka, który ma niejawny charakter, thistaki jak Java, ale gdzie funkcje mogą istnieć niezależnie od obiektów takich jak Python. Prowadzi to do wielu nieporozumień dotyczących tego, co thisodnosi się, gdy funkcje są przekazywane i wywoływane w różnych kontekstach. Wielu instynktownie uważa, że thismusi odnosić się do pewnych wewnętrznych właściwości funkcji, podczas gdy jest ona w rzeczywistości określana wyłącznie przez sposób jej wywoływania. Wierzę, że posiadanie thistak wyraźnego parametru jak w Pythonie sprawiłoby, że byłoby to mniej skomplikowane.

Kilka innych korzyści z jawnego selfparametru:

  • Dekoratory to tylko funkcje, które obejmują inne funkcje. Ponieważ metody są tylko funkcjami, dekoratorzy działają równie dobrze na metodach. Gdyby istniało jakieś ukryte ja, dekoratorzy nie pracowaliby transparentnie nad metodami.

  • Metody klas i metody statyczne nie przyjmują parametru instancji. Metody klas przyjmują klasę jako pierwszy argument (zwykle nazywany cls). Wyraźne selflub clsparametry znacznie wyjaśniają, co się dzieje i do czego masz dostęp w metodzie.

2) Dlaczego zmienne instancji muszą zawsze być kwalifikowane za pomocą „ self.?

W Javie nie trzeba poprzedzać zmiennych członkowskich znakiem „ this.”, ale w Pythonie „ self.” jest zawsze wymagane. Powodem jest to, że Python nie ma jawnej składni do deklarowania zmiennych, więc nie byłoby sposobu, aby stwierdzić, czy x = 7należy zadeklarować nową zmienną lokalną lub przypisać do zmiennej członka. Określenie self.rozwiązuje tę dwuznaczność.

JacquesB
źródło
Odwołanie do niejawnej zmiennej członka (bez self., np. Java) jest zasadniczo niezgodne z regułami określania zakresu, a kiedy trzeba tam być jawnym, domniemanie dotyczące parametru nie ma już większego sensu.
Jan Hudec
@JanHudec: Dobrze, punkt. Dodałem go do odpowiedzi.
JacquesB
6

Istnieje dość prosty powód, dla którego AFAIK nie został tak naprawdę poruszony w duplikacie strony krzyżowej, ani tutaj: Python zaczął jako język proceduralny. Oparty był na ABC, również języku proceduralnym.

Orientacja obiektowa została dodana później, a kiedy została dodana, Guido van Rossum chciał dodać minimalną liczbę możliwych funkcji, aby zachować prostotę projektowania Pythona. Python ma już dicts i funkcje, więc po co dodawać do języka coś zupełnie nowego, skoro obiekt może być po prostu dictgniazdem, a klasa może po prostu być dictfunkcją? Metodę można interpretować jako częściowo zastosowaną funkcję, która zamyka pojedynczy wyróżniony argument. I tak właśnie metody są implementowane w Pythonie: nie są. Są to tylko funkcje, które otrzymują dodatkowy wyróżniający się argument.

Jörg W Mittag
źródło
Wierzę, że Python obsługiwał OO i miał klasy i dziedziczenie od pierwszej wydanej wersji. Przynajmniej tak mówi mi wikipedia. Ale proces van Rossums w początkowym projektowaniu języka mógł przebiegać tak, jak to opisałeś.
JacquesB
Dziękuję za link, to naprawdę interesująca lektura.
JacquesB
2

Oto moje wnioski w oparciu o powyższe odpowiedzi i czytanie własnych Guido wędrówki na ten temat:

Wielki pomysł

Funkcje są ważnymi elementami składowymi Pythona (a raczej powinniśmy powiedzieć, że jedynymi), w rzeczywistości naśladujemy OOP za pomocą funkcji.

Biorąc pod uwagę, że klasa jest jedynie słownikiem funkcji, w związku z tym możemy dołączyć dowolną funkcję do dowolnej klasy w czasie wykonywania. Zasadniczo to z powodu potrzeby przerzucania funkcji w czasie wykonywania możemy robić rzeczy takie jak Monkey Patching . Tutaj selfparametr wspierający parametryczny polimorfizm.

vivek
źródło
1
Zachęcamy do odpowiedzi na pytania, na które odpowiedź jest wysokiej jakości. Kiedy porównuję twoją odpowiedź tutaj z innymi odpowiedziami, zastanawiam się, dlaczego czułeś się, jakbyś musiał to dodać. Pozostałe odpowiedzi są bardziej szczegółowe i dodają więcej szczegółów niż to, co robi twoja odpowiedź.
1
@ GlenH7 odpowiedź była tylko dla mojego odniesienia, ponieważ za każdym razem nie mogę przyjść i przeczytać odpowiedzi wszystkich od nowa. Jeśli chodzi o jakość, powiedz mi, czy jakieś informacje wprowadzają w błąd. W każdym razie zazwyczaj czekam 2-3 dni na zaakceptowanie jakiejkolwiek odpowiedzi.
vivek
Głosowanie w dół staje się tanią walutą i wszyscy tutaj wydają je obiema rękami, nie znając ich znaczenia. Czy zdajesz sobie sprawę, że jeśli ktoś tu przyjdzie, zobaczysz, że głosowanie w tej głosowaniu jest błędne!
vivek