Klasy, Przestrzenie nazw, Nonlocal, Global i Local. Czy Python ma zmienne prywatne ?

Cześć!

Fajnie Cię widzieć. Dzisiaj zajmiemy się tematem klas. Python jest językiem obiektowym, nie zawierającym w sobie jednak niektórych elementów, które zawierają inne języki. Nie posiada zmiennych typu prywatnych, chronionych. I Nie. Nie jest prawdą, że zapis __ sprawia, że pole staje się superprivate. W Pythonie nie ma sensu komplikować sobie życia modyfikatorami dostępu. Ale o tym może później.

Jak wygląda składnia klasy ?

class nazwa():
 ...statements...

Klasa to przepis jak zrobić dany obiekt. Przepis to jednak jeszcze nie wykonanie, dlatego musimy coś w środku umieścić po czym stworzyć obiekt, który będzie już miał określone cechy. W Pythonie wygląda to podobnie jak w C++. Jest jednak kilka różnic. Zanim to zrobimy, zróbmy coś lubicie, czyli zajmiemy się przykładem

class MyClass():
def name_space(value):
def show():
nonlocal value
value = value + 1
return value
show()
print(value)
return value

for i in range(3):
x = MyClass.name_space(i)

Do kodu doszły nowe informacje, które nie są celem samym w sobie, a są ukazaniem składni Pythona w przykładzie. Zacznijmy od początku. Funkcje w Pythonie definiujemy przez słówko kluczowe def. Żeby wpisać coś w klasie potrzebujemy tabulatora w jej wnętrzu. Tzw. wcięcia. Zwanego po angielsku indented, które odróżnia część globalna od tej która ma się znajdować w środku klasy. Warte wyjaśnienia jest słówko kluczowe nonlocal, które nie występowało wcześniej w materiałach, a jest tutaj użyte. Czym ono jest ? Noo... Jak sama nazwa mówi jest czymś co nie jest lokalna, ale nie jest też czymś globalnym za które odpowiada słówko kluczowe global. Czym więc to jest, zapytasz. Już służę z odpowiedzią. Słówko kluczowe nonlocal jest czymś pomiędzy przestrzenią nazw globalną, a lokalną. Wyobraźmy sobie że mamy funkcję x, a w jej środku funkcja y. Chcemy w funkcji x użyć coś co znajduje się w funkcji y. Będziemy mogli jej używać po wywołaniu funkcji y w x, która przysłoni nazwy znajdujące się lokalnie o tej samej nazwie i zmieni ich wartość na taką jaką ma zmienna nonlocal.

Wypróbujmy teraz słówka globalnego global w kontekście klas:

glVar = 150
class My():
    glVar +=1
    print(glVar)
print(glVar)

Hmm, ależ cóż to ? Zmieniliśmy w My() zmienną glVar, a ona nie zmieniła swojej wartości ? (Warto nadmienić, że interpreter kodu działa linijka po linijce wyświetlając to co znajduje się w klasie i dzięki temu możemy podziwiać wypisanie bez podziału na funkcje).

Co możemy zrobić z tym cudem, żeby zmieniło to na wartość która Nam się podoba ? Użyjmy słówka kluczowego global! Kod po modyfikacji wygląda w następujący sposób:

glVar = 150
class My():
    global glVar
    glVar +=1
    print(glVar)
print(glVar)

No! Teraz to wygląda znacznie ciekawiej. Pozostaje tylko jeszcze jedna kwestia. W tytule wspomniane jest to, że zajmiemy się local, a nie zajęliśmy się jeszcze, więc co z Nimi ? Cóż... Dobrze że przypomniałeś. Zajmiemy się Nimi właśnie teraz. Więc czym jest słowo kluczowe local ? Jak nie trudno domyślić się jest to coś lokalnego, coś zamkniętego. Z uwagi na to, że otrzymuję od Was maile, że zamiast słów, wolicie przykładów szum, także zgodnie z Waszym życzeniem:

class MyL():
    def changeLocal():
        glVar=15
    print(glVar)

Jak widać zmiana glVar nie następuje, gdyż Python tworzy nową zmienną lokalną, obejmujące w intendent blocks. Czyli od wcięcia do jego końca.

PRZESTRZENIE NAZW:

Dochodzimy właśnie do najważniejszej rzeczy, o której chciałem wspomnieć w Pythonie. Są to przestrzenie nazw. Python sprytnie zarządzania przestrzeniami, unikając konfliktu nazw, które mogłyby występować, gdyby występowały zmienne o tej samej nazwie, lub takie same klasy, itd. Wyobraźmy, że piszemy program liczący 10 tysięcy linii. Piszemy go z kolegą. Jesteśmy odpowiedzialni za napisanie funkcji, która zmienia coś w kodzie. Piszemy więc funkcję: change():....
I zmieniamy to co chcemy zmienić. Tymczasem Nasz kolega też postanowił, że coś zmieni i napisze funkcję o nazwie change. Mamy problem. Co kompilator(w przypadku Pythona interpreter, co eliminuje ten problem, ale o tym później) powinien wybrać ? Komputer nie wie, czy chcemy to wywołać Naszą funkcję, czy kolegi. Python jest przed tym zabezpieczony w zasadzie na trzy sposoby. Pierwszy mniej bezpieczny, to interpreter, która automatycznie wywołuje najmłodszą funkcję. 

def change():
    print(1)
def change():
    print(2)

change()


Drugim i znacznie ważniejszym mechanizmem są moduły, o których wspomniałem w poprzednim poście. 

A jaki ma to związek z klasami zapytasz. Celne pytanie. Klasy podobnie jak cały kod może mieć te same problemy, ale szansa na to jest znikoma, gdyż nikt raczej nie tworzy klasy, która miałaby mieć dużo funkcji. Lub zmiennych. W tym momencie warto wyjaśnić sobie popularne w Pythonie pojęcie "Superprivate", które NIE ISTNIEJE. To po prostu nowa przestrzeń nazw i tyle. O co w zasadzie tyle szumu ? Pokażmy to na przykładzie:

class MySuperPrivateClass():
    def __mySuperPrivateVar(self):
        return 15
print(MySuperPrivateClass()._MySuperPrivateClass__mySuperPrivateVar())

W przypadku double underscore, czyli w przypadku nadania zmiennej nazwy zaczynającej się od podwójnego podkreślenia, trafia ona do specjalnej przestrzeni nazw, która znajduje się w tej klasie po dopisku do Niej: _ znaku pojedynczego podkreślenia i tyle. 

A co w przypadku pojedynczego podkreślenia ? Nie otrzymuje ona po prostu domyślnie parametru self, którym zajmiemy się innym razem. Self znaczy po prostu że odnosimy się do tego co znajduje się w tej klasie, ale o tym innym razem. Generalnie ta przestrzeń nazw powinna służyć klasom dziedziczącym. Żeby to zilustrować dobrze jest pokazać Wam przykład, żebyście mogli na spokojnie zobaczyć w czym jest rzecz i zająć się tym na spokojnie:

class MySuperPrivateClass():
    def _fun():
        return 100

print(MySuperPrivateClass._fun())


To byłoby na tyle w przypadku klas. Konstruktorami i atrybutem self zajmiemy się przy innej okazji. Z uwagi na to, że temat self jest bardzo ważny warto nadmienić, że Python przy tworzeniu jakiekolwiek funkcji dodaje zamienia domyślną składnię klasy na taką, której pierwszy atrybut zawiera swój własny obiekt, dzięki czemu pisząc słówko self wywołujemy metody, czy zmienne mając na myśli tylko ten obiekt. I nic obiekt. Interpreter Pythona porusza się linia po linii, przez co jest to bardzo ważne, gdyż nie mając takiej pewności, Python mógłby zmienić inne zmienne, które nazywają się tak samo.

Komentarze

Popularne posty z tego bloga

Co dokładnie oznacza std::cout<<"Witaj Swiecie!"<<'\n''; w C++ ?

Przeciążanie operatorów w C++