Metode speciale în Python

    Îmi cer scuze că nu am scris de vreo 2 săpt. Am fost plecat și am fost în pană de idei

    Clasele din Python pot avea, pe lângă metodele normale, care se apelează direct, și metode speciale, care nu prea sunt apelate direct (deși se poate), ci Python le apelează atunci când are nevoie de ele. Un exemplu de asemenea metodă specială, pe care toată lumea îl știe, este __init__, care definește con­struc­torul clasei. Acesta nu îl apelăm noi direct, ci Python îl apelează în momentul în care creăm o clasă nouă. Un alt exemplu destul de cunoscut (cel puțin dacă sunteți studenți la UBB) este __str__, care este apelat în momentul în care obiectul nostru este într-un context care necesită un string: fie că este afișat pe ecran, fie că e concatenat cu un alt string, etc.

    Mai jos vă mai prezint câteva metode faine, care mi-au fost de folos recent (sau doar mi-au atras atenția în timp ce citeam doc­u­men­tația):

    1. __getattr__ și __setattr__ - prima se apelează în momentul în care se apelează un atribut inexistent al clasei. Iar al doilea se apelează atunci când faci o operație de atribuire la un atribut inexistent al clasei. Și apoi poți face ce vrei tu cu ele :D Exemplu:

        class test:
            dictionary = { }
    
            def __getattr__(self,name):
                print("You accesed "+name)
                print(self.dictionary[name])
    
            def __setattr__(self,name,value):
                print('you set the value of '+name+' to '+str(value))
                self.dictionary[name] = value
    
        a = test()
        a.test = 1
        a.other = 'an attribute'
        a.test
        a.other

    În acest exemplu, atunci când accesăm parametrii, aceștia sunt frumos afișați pe ecran. Când dăm o valoare unui atribut, acesta nu se salvează în dicționarul obiectului, ci se salvează în dicționarul numit dictionar. La ce se poate folosi aceasta? Să zicem că nu știm, în momentul în care scriem codul, ce atribute o să avem (poate vrem să scriem un obiect care reprezintă o tabelă oarecare a unei bazei de date) și atunci folosim __getattr__ și analogul ei, __setattr__, ca să stocăm și să accesăm atributele altundeva (de exemplu într-un dicționar al clasei). Cu __getattr__ putem să permitem apelarea oricărei metode ale unei clase, chiar dacă aceasta nu este denumită explicit:

        class testFunction:
    
            def __getattr__(self,name):
                def function():
                    print( "you called a method named "+name)
                return function
    
        b = testFunction()
        b.method1()
        b.method2()

    În acest caz funcția getattr returnează o funcție care afișează pe ecran numele funcției apelate. Eu am folosit asta pentru a crea settere/gettere pentru un atribute ne­cunos­cute la scrierea codului. 2. __iter__, __next__, __getitem__ și __setitem__. - acestea ne permit să facem obiecte iterabile. De exemplu dacă avem o clasă care este un container de obiecte, putem permite iterarea ușoară în for-uri peste acel container declarând primele două metode și putem accesa obiecte date prin indecși sau slice-uri prin ur­mă­toarele două. Exemplu:

        class iterableClass:
    
            def __init__(self):
                self.list = [1,2,34,5]
                print(self.list)
    
            def __iter__(self):
                self.index = -1
                return self
    
            def __next__(self):
                if self.index == len(self.list)-1:
                    raise StopIteration
                self.index +=1
                return self.list[self.index]
    
            def __getitem__(self,key):
                return self.list[key]
    
        c = iterableClass()
        for i in c:
            print (i)
        print(c[0:2])

    That's just the tip of the iceberg. Se pot emula tipuri numerice, se poate schimba tipul clasei (ce valoare dă pentru type() ), se poate schimba metoda de stocare a datelor în clasă etc. All in all, Python's awesome, and I'm barely scratching at it's surface.