Inteligenta Artificiala

Laboratorul 3

(Deadline: -)

Daca nu sunteti logati exercitiile nu se mai afiseaza.

Module

Crearea modulelor proprii

Orice program este de fapt un modul.

Un modul poate fi importat in alt program cu instructiunea import nume_modul

Din acest moment putem folosi clasele si functiile definite in modul.

Putem importa si un singur element din modul: from nume_modul import element

In momentul importarii unui modul, acesta se si executa. Putem verifica daca se executa codul in cadrul modulului (si nu printr-un import) testand: if __name__ = "__main__":

Clase

Pentru a crea clase folosim cuvantul cheie class.

class Dreptunghi:

Constructorul este metoda __init__(). Ca si alte functii ce tin de instanta, va primi ca parametru chiar obiectul curent. Rolul constructorului e sa initializeze obiectul.

Pentru a accesa proprietati in interiorul metodelor de instanta (cum e si constructorul) folosim identificatorul self. Atentie, aceasta e doar o conventie, putem sa ii dam orice nume, insa pentru o intelegere mai buna a codului folosim self. Acest lucru se intampla deoarece in metodele de instanta primul parametru este mereu obiectul.

Tot o astfel de conventie avem si pentru metodele de clasa unde folosim prin conventie identificatorul cls pentu parametrul care ar trebui sa primeasca clasa.

Daca dorim sa stergem proprietati ale obiectelor sau chiar obiecte folosim del.

class Dreptunghi:
    """O clasa care memoreaza un dreptunghi format din caractere"""
    #atribute de clasa (statice)
    liniiSeparatoare=True;
    simbolSeparator="-"
    dimLinieSeparatoare=15
    
    
    def __init__(self, inaltime, latime, simbolContinut, simbolContur=None):
        #atribute de instanta
        self.inaltime = inaltime
        self.latime = latime
        self.simbolContinut = simbolContinut
        self.simbolContur = "#" if simbolContur is None else simbolContur
        
    def arie(self):
        return self.inaltime*self.latime;
    
    @classmethod
    def afisSimbolContur(cls):
        print('Simbol separator: '+ cls.simbolSeparator)

    @classmethod
    def afisNumeClasa(cls):
        print('Nume clasa:\n'+ cls.__name__)

    
    @classmethod
    def afisDoc(cls):
        print('Informatii clasa:\n'+ cls.__doc__)
        
        

        
        
    @staticmethod
    def afisLinie(n,simbol):
        print(n*simbol)

        
    #operatori
    
    def __eq__(self, dr):
        return (self.inaltime==dr.inaltime and self.latime==dr.latime)
    
    #observati ca nu trebuie sa ne raportam la aceleasi criterii, chiar daca in general nu este indicat
    def __lt__(self, dr):
        return self.arie()<dr.arie()
        

    def __lte__(self, dr):
        return self.arie()<=dr.arie()
        
    def __gt__(self, dr):
        return self.arie()>dr.arie()        
        
        
        
        
        
    #definesc ce va returna repr(obiect)
    def __repr__(self):
        sir="Prop clasa:\n"
        for (k,v) in self.__class__.__dict__.items() :
            sir+="{} = {}\n".format(k,v)
        sir="Prop instanta:\n"
        for (k,v) in self.__dict__.items() :
            sir+="{} = {}\n".format(k,v)
        return(sir)
        
    #definesc ce va returna str(obiect)    
    def __str__(self):
        sir=self.simbolContur*self.latime+"\n";
        for i in range(self.inaltime-2):
            sir+=self.simbolContur+self.simbolContinut*(self.latime-2)+self.simbolContur+"\n"
        sir+=self.simbolContur*self.latime+"\n"
        return(sir)
        
    def afis(self):
        if (self.liniiSeparatoare):
            self.afisLinie(self.dimLinieSeparatoare,self.simbolSeparator)
        print(d)
        if (self.liniiSeparatoare):
            self.afisLinie(self.dimLinieSeparatoare,self.simbolSeparator)
    


class Secventa:
    secventa=0
    def __init__(self):
        self.secventa+=1
        
        
class HartaJoc(Dreptunghi,Secventa):
    
    simbolObstacol="@"
    __matrice__=[]
    
    def __init__(self,nume,inaltime, latime, simbolContinut, simbolContur=None):
        Dreptunghi.__init__(self,inaltime, latime, simbolContinut, simbolContur)
        Secventa.__init__(self)
        sirDr=str(self)
        __matrice__=[[] for i in range(inaltime)]
        self.nume=nume
    
            
    def afis(self):
        if (self.liniiSeparatoare):
            self.afisLinie(self.dimLinieSeparatoare,self.simbolSeparator)
        for linie in __matrice__ :
            print(linie.join())
        if (self.liniiSeparatoare):
            self.afisLinie(self.dimLinieSeparatoare,self.simbolSeparator)        
            
if __name__=="__main__":
    d1=Dreptunghi(5,10,'.')
    print("Am creat dreptunghiul d1=Dreptunghi(5,10,'.')")
    ##########################################
    print("Ce afiseaza print(d1)")
    print(d1)
    ##########################################
    print("Ce afiseaza print(str(d1))")
    print(str(d1))
    ##########################################
    print("Ce afiseaza print(repr(d1))")
    print(repr(d1))
    ##########################################
    d2=Dreptunghi(4,4,'.')
    ##########################################
    Dreptunghi.afisSimbolContur()
    Dreptunghi.afisNumeClasa()
    Dreptunghi.afisDoc()
    ##########################################
    print("Am creat harta h=HartaJoc('campie',5,10,'.')")
    h=HartaJoc('campie',5,10,'.')
    ##########################################
    print("Ce afiseaza print(h)")
    print(h)
    ##########################################
    print("Ce afiseaza print(repr(h))")
    print(h)

Mostenirea claselor se face prin: class ClasaDerivata(ClasaBaza):

Functia dir()

Functia dir() afiseaza toate proprietatile si metodele unui obiect.

>>> x=range(1,10)
>>> x
range(1, 10)
>>> dir(x)
['__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index', 'start', 'step', 'stop']
>>>

Expresii regulate

https://docs.python.org/3/library/re.html

Fisiere si directoare (modulul os)

https://docs.python.org/2/library/os.html

Modulul sys

https://docs.python.org/2/library/sys.html

Modulul datetime

https://docs.python.org/3/library/datetime.html

Modulul time

Acest modul este util în special pentru a calcula timpul de execuție a unei anumite zone de cod. Exemplu: t1=time.time()
#zona de cod pentru care dorim sa calculam timpul
t2=time.time()
print(t2-t1)#in secunde

Timeout in python

Uneori dureaza foarte mult executia unei functii, si am vrea daca depaseste un anumit interval de timp, sa o oprim (consideram ca peste acel interval de timp, rezultatul nu mai este relevant sau este oricum prea costisitor de obtinut).

Putem pune un timeout pe executia unei functii folosind modulul stopit.

Se instaleaza in mod obisnuit cu pip: pip install stopit

Pentru a folosi stopit, "decoram" functia (scriem pe randul imediat de deasupra definirii ei) cu: @stopit.threading_timeoutable(default="valoare returnata de functie cand intra in timeout") Valoarea data parametrului default este, asa cum scrie si mai sus, cea returnata de functie in caz de timeout (in felul asta ii putem da o valoare speciala ca sa verificam daca a intrat in timeout sau nu).

De asemenea, functia se va apela cu un parametru suplimentar, numit timeout in care se va specifica numarul de secunde dupa care sa intrerupa functia, in cazul in care nu si-a terminat singura executia. Parametru timeout nu trebuie precizat in definirea functiei. De exemplu, daca functia e definita: def f(a,b,c):
...
si are deocratia @stopit.threading_timeoutable, se va apela cu 4 parametri (primele 3 valori fiind asociate cu a, b si c, iar a 4 fiind timeout-ul de care se ocupa "decoratia" functiei): f(10,20,30, timeout=10):
...
In acest exemplu, executia functiei va fi intrerupta fortat dupa 10 secunde, daca functia nu s-a terminat inca.

De exemplu, avem codul: import stopit


@stopit.threading_timeoutable(default="intrat in timeout")
def functie(n):
    j=0;
    for i in range(n):
        print(i, end=" ")
    return "functie finalizata"
        
        
print("#"*10)
rez=functie(1000,timeout=1)
print("\nRezultat functie: {}".format(rez))
print("#"*10)
rez=functie(10,timeout=1)
print("\nRezultat functie: {}".format(rez))
Cu outputul: ##########
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
Rezultat functie: intrat in timeout
##########
0 1 2 3 4 5 6 7 8 9
Rezultat functie: functie finalizata

Se observa ca la primul apel, functia a intrat in timeout (a durat mai mult de o secunda, cat am precizat) si a fost intrerupta dupa ce a trecut acel timp. Din aceasta cauza functia a returnat valoarea din parametrul default. In al doilea caz, functia a returnat valoarea efectiva din return-ul ei (fiindca nu a mai intrat in timeout).

Este posibil cand rulati programul sa nu va dea timeout in primul caz, daca aveti un calculator foarte rapid. Pentru a vedea efectul, cresteti numarul de iteratii, de exemplu puneti 100000.

Tema