Obowiązkowe this i self

0

Ugh, jak mnie to irytuje. Niestety troszkę czasu spędzonego w C# i C++ dało się we znaki. Tam this jest implicite.
Ale nie, Javascript a tym bardziej Python upierają się, że trzeba this (czy tam self) wstawiać explicite. Ciągle o tym zapominam.

Jeszcze w JS jest pół biedy, jak zapomnę this to obrywam po prostu wyjątkami. Ale w Pythonie wymagane jest, by nawet każda metoda explicite przyjmowała self jako pierwszy argument!! Raz mi się zdarzyło zapomnieć o wprowadzeniu self do listy argumentów. I jak głupi zamiast metoda(self, *args, **kwargs) dałem metoda(*args, **kwargs) A potem zupełnie gdzie indziej mi wyjątki leciały, bo args[0] było czymś zupełnie innym, niż miało być.

!!!

Dlaczego w zasadzie tak wymagają? Co jest złego w domyślnym this?! Naprawdę nie widzę? Ale może bardziej doświadczeni widzą mankamenty domyślnego this?

2
The Zen of Python
Beautiful is better than ugly.
> Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

https://www.python.org/dev/peps/pep-0020/

1

Explicit is better than implicit.

Naprawdę? W Pythonie nie ma różnicy między zmianą, a tworzeniem zmiennej. Obojętnie czy lokalnej czy pola w obiekcie. Niejawność na każdym kroku.

Przejrzałem parę artykułów. Jeden od GvM: http://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html Napisał tam, że jawny self umożliwia taką konstrukcję:

# Define an empty class:
class C:
   pass

# Define a global function:
def meth(myself, arg):
   myself.val = arg
   return myself.val

# Poke the method into the class:
C.meth = meth

To jakiś poważny monkey-patching.

Drugi argument to że niejawny self oznacza problem przy stosowaniu dekoratorów. To wskazuje na ułomność języka. C++, Java, C# itd rozróżniają między metodą statyczną a niestatyczną poprzez słowo kluczowe static i nie ma problemu.

W tym artykule https://docs.python.org/3/faq/design.html#why-must-self-be-used-explicitly-in-method-definitions-and-calls jest też argument, że bez jawnego selfa nie wiadomo czy mamy do czynienia ze zmiennymi lokalnymi czy polami instancji. Ruby radzi sobie z tym poprzez użycie @ przez nazwami składowych instancji. Niby dalej jawnie rozróżnia, ale @ jest znacznie krótsze niż self.. GvM jednak tego nie lubi i nie skorzysta z tego http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html :

Another possible solution would have been to make instance variables lexically distinct. For example, having instance variables start with a special character such as @ (an approach taken by Ruby) or by having some kind of special naming convention involving prefixes or capitalization. Neither of these appealed to me (and they still don't).

W tym artykule o historii Pythona GvM jawnie przyznaje się do tego, że po prostu chciał mieć prosty interpreter i tak tworzył język by się jak najmniej narobić. To był jeden z głównych priorytetów. Drugi to, jak już wspomniałem, własne widzimisię. Gdyby GvM się chciało to zredukowałby ilość jawnych selfów do poziomu np takiego jaki jest w Ruby.

1
spartanPAGE napisał(a):
The Zen of Python
Flat is better than nested.
Namespaces are one honking great idea -- let's do more of those!

Czy tu nie ma sprzeczności?

Wibowit napisał(a):

W tym artykule https://docs.python.org/3/faq/design.html#why-must-self-be-used-explicitly-in-method-definitions-and-calls jest też argument, że bez jawnego selfa nie wiadomo czy mamy do czynienia ze zmiennymi lokalnymi czy polami instancji.

Jakoś nie przypominam sobie, bym się kiedykolwiek na tym naciął w takim np. C#.

0

Nie żebym bronił pythona, bo nie trawię tego języka, ale z jawnością deklaracji akurat się zgadzam, nie lubię jak jakieś szczegóły dzieją się niejawnie w tle. Może ten przypadek jest banalny i akurat łatwy i intuicyjny, ale wiadomo, od tego się zaczyna, a potem masz takie konstruktory i destruktory w C++. :p Myślę że takie upodobanie może być powiązany z przekoaniem, że kod źrółowy przede wszystkim do czytania go, a swoją drogą do kompilowania. C# akurat z tego co pamiętam stosuje przeciwną filozofię, napisz łatwo, kto inny będzie kląć naprawiając to. :p

0
elwis napisał(a):

od tego się zaczyna, a potem masz takie konstruktory i destruktory w C++. :p

Co w tym złego, że obiekt sam po sobie sprząta, kiedy kończy się jego czas życia?

EDIT: Nawet jak jest garbage collector, to i tak czasem coś destruktoropodobnego jest niezbędne (nie wszystkie zasoby to pamięć); ale jakie lepsze rozwiązanie, gdy GC nie ma?

0
kmph napisał(a):
spartanPAGE napisał(a):
The Zen of Python
Flat is better than nested.
Namespaces are one honking great idea -- let's do more of those!

Czy tu nie ma sprzeczności?

Jak dla mnie to "Flat is better than nested" brzmi prawie jak "Inheritance over composition" :)
Wiele reguł jest uznaniowych, np:
Beautiful is better than ugly. vs Explicit is better than implicit. (zasrywanie kodu selfami nie jest piękne)
Now is better than never. vs Although never is often better than *right* now.
Special cases aren't special enough to break the rules. vs Although practicality beats purity.

kmph napisał(a):
Wibowit napisał(a):

W tym artykule https://docs.python.org/3/faq/design.html#why-must-self-be-used-explicitly-in-method-definitions-and-calls jest też argument, że bez jawnego selfa nie wiadomo czy mamy do czynienia ze zmiennymi lokalnymi czy polami instancji.

Jakoś nie przypominam sobie, bym się kiedykolwiek na tym naciął w takim np. C#.

Nie rozwinąłem myśli, która była rozwinięta w przytoczonych artykułach. Otoż chodzi o to, że jak np mamy przypisanie a = 5 w metodzie klasy to przy niejawnym selfie nie wiadomo by było czy tworzymy zmienną (bądź podmieniamy jej zawartość, bo składnia do obu jest taka sama w Pythonie) lokalną czy aktualnego obiektu. Stąd trzeba jawnie rozróżniać. W Pythonie a = 5 dotyczy zmiennej lokalnej, podobnie jak w Ruby. By modyfikować zmienną aktualnego obiektu w Pythonie stosujemy self.a = 5, a w Ruby @a = 5. W Javie, C#, C++ itd nie ma problemu z niejawnym thisem/ selfem/ zwał jak zwał, bo mamy inną składnię do tworzenia i modyfikowania zmiennej. Tworzenie to np int a; bądź int a = 5, a modyfikacja to np a = 5. Można by zrobić tak, że przy tworzeniu/ modyfikacji zmiennej wymagany jest jawny self, a przy odczycie już nie, ale to z drugiej strony wprowadziłoby pewną niespójność.

0
Wibowit napisał(a):

Nie rozwinąłem myśli, która była rozwinięta w przytoczonych artykułach. Otoż chodzi o to, że jak np mamy przypisanie a = 5 w metodzie klasy to przy niejawnym selfie nie wiadomo by było czy tworzymy zmienną (bądź podmieniamy jej zawartość, bo składnia do obu jest taka sama w Pythonie) lokalną czy aktualnego obiektu. Stąd trzeba jawnie rozróżniać. W Pythonie a = 5 dotyczy zmiennej lokalnej, podobnie jak w Ruby. By modyfikować zmienną aktualnego obiektu w Pythonie stosujemy self.a = 5, a w Ruby @a = 5. W Javie, C#, C++ itd nie ma problemu z niejawnym thisem/ selfem/ zwał jak zwał, bo mamy inną składnię do tworzenia i modyfikowania zmiennej. Tworzenie to np int a; bądź int a = 5, a modyfikacja to np a = 5.

Dalej nie widzę problemu jednak. Nie pamiętam, jak jest w Pythonie, for the sake of argument załóżmy więc, że jest jak w JS: każda zmienna niezdefiniowana ma wartość undefined. Koci mi się, że w Pythonie może są zamiast tego wyjątki, ale to bez znaczenia. Tak więc:

  • Jeśli tylko odczytujemy wartość zmiennej:
  • Bez prefiksów: w pierwszej kolejności odczytujemy wartość zmiennej lokalnej o ile nie jest ona undefined; jeśli jest undefined, odczytujemy wartość pola klasy
  • Z prefiksem self: oczywiście tylko pole klasy
  • Z prefiksem local, który można wprowadzić jako słowo kluczowe: tylko lokalna, dostajemy undefined nawet, jeśli jest pole klasy nie undefined o tej nazwie
  • Jeśli jest przypisanie a = 5:
  • Bez prefiksów: O ile zmienna lokalna oraz pole klasy jest undefined: tworzymy zmienną lokalną; o ile zmienna lokalna jest zdefiniowana, przypisujemy do niej, niezależnie od pola klasy; o ile zmienna lokalna jest undefined, a pole klasy jest zdefiniowane, przypisujemy do pola klasy. Czyli: Preferujemy zmienną lokalną ponad polem klasy, ale preferujemy także przypisanie ponad zadeklarowaniem
  • Z prefiksami local czy self: Oczywiście przypisujemy do zmiennej lokalnej / pola klasy bez względu na cokolwiek
0

Jako człowiek, który dużo czasu spędził w Pythonie, w swoich klasach w C++ zawsze jawnie używam this. Nie muszę się martwić, że argument metody ma taką samą nazwę, co pole klasy, albo że zadeklaruję sobie w metodzie zmienną o tej samej nazwie. Nie muszę się uciekać do wstawiania jakichś podkreśleń rodzaju int variable_, żeby uniknąć takich problemów.

Poza tym, imho, poprawia to czytelność kodu. Np. przy podobnych do siebie nazwach jakiejś zmiennej globalnej i pola klasy, za pół roku jak się spojrzę na kod, to będę wiedział, o które z nich mi chodziło.

Kwestia przyzwyczajenia.

2

Nie muszę się martwić, że argument metody ma taką samą nazwę, co pole klasy, albo że zadeklaruję sobie w metodzie zmienną o tej samej nazwie.

Na pewno?

Załóżmy, że najpierw masz taką metodę:

void setX(int x) {
  this.x = x;
}

Potem usuwasz argument i kod się dalej kompiluje:

void setX() {
  this.x = x;
}

Ewentualnie można to samo zrobić ze zmienną lokalną. Najpierw masz:

void doSomething() {
  int x = 5;
  this.x = x + 2;
}

Usuwasz (bądź komentujesz kawałek kodu) i deklaracja x wylatuje:

void doSomething() {
  this.x = x + 2;
}

Przy dodawaniu zmiennych niby się nie musisz "martwić", ale przy usuwaniu już tak.

Jeśli nie programujesz jakimś badziewiem, to sensowne IDE powinno cię ostrzec przed przesłanianiem zmiennych. MSZ lepiej polegać na porządnym IDE niż zaśmiecać sobie kod.

0

Top 10 tokenów typu NAME w źródle (*.py) CPython 3.8.0a1:

1   193332  self
2   51859   def
3   32727   assertEqual
4   29313   if
5   21225   None
6   20643   return
7   16043   in
8   12122   for
9   11089   class
10  10789   import

self to niezły rak.

0

JavaScript jest językiem opartym na prototypach a nie na klasach. this ma w nim inne znaczenie. W Pythonie nie trzeba używać self.

class Person:
  def __init__(s, name):
    s.name = name

  def show(s):
    print(s.name)

person = Person("myname")
person.show()

1 użytkowników online, w tym zalogowanych: 0, gości: 1