Algoritmusok Python nyelven

4. Előadás, Objektumorientált programozás 1.

2020. március 5.

Technikai információk

Diák elérhetősége:

damasdigabor.web.elte.hu/teaching

Óra kezdés: 14:00

In [ ]:
a=5
def f():
    print(a)
f()
In [ ]:
a=5
def f():
    a=a+1
    print(a)
#f()

Namespace (névtér)

A namespace a változó nevek és az objektumok közti leképezés. Pl:

  • beépített nevekhez (abs(), sorted(), int stb...) tartozik egy namespace
  • globális namespace: ide kerülnek a függvényeken kívül létrehozott változók
  • lokális namespece: minden függvény létrehoz egy saját namespacet, először abban keres

    Különböző namespacekben szerepelhet egyező név!

In [ ]:
# Ez egy szándékosan zavaróan megírt kód. 
a=5                       # a globális namespaceben 'a' az 5-re fog mutatni
def foo(a):               # ez már egy másik `a`, ami a foo() függvény namespaceben él
    print(a+1)            # itt a foo()-hoz tartozó 'a'-ra hivatkozunk.   
    def belsofugveny(a):  # ez egy harmadik 'a' változó, ez már a belsofugveny()-hez tartozik
        print(a+5);       # itt a belsofugveny()-hez tartozó 'a'-ra hivatkozunk.  
    belsofugveny(a)       # itt a foo()-hoz tartozó 'a'-ra hivatkozunk.    
   
foo(10)
a                         # itt a globális 'a'-ra hivatkozunk.   

Scope

Minden namespacehez tartozik egy scope. A scope a kódnak az a része, ahol a neveket automatikusan abban az adott namespaceben keresi a program.

In [ ]:
a=5                       #
                          #
def foo(a):                   # 
    print(a+1)                #    
                              #   
    def belsofugveny(a):          # 
        print(a+5);               #
                              #
    belsofugveny(a)           # 
                          #
foo(10)                   #  
a                         #    

Hogyan érjük el egy másik namespaceben lévő objektumokat?

  • nonlocal valnev megmondja, hogy eggyel kintebbi scopeban keressen.
  • global valnev megmondja, hogy a globális scopeban keressen.
In [ ]:
def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("lokális értékadás után:", spam)
    do_nonlocal()
    print("nonlocal kulcsszó után:", spam)
    do_global()
    print("global kulcsszó után:", spam)

scope_test()
print("globális scopeban:", spam)
In [ ]:
 

Programozási paradigmák

Sokféle van, például

  • Procedurális
  • Funckionális
  • Objektumorientált

Példa: Főzés vs matek vs autók

Mikor melyiket válasszuk?

Python

  • többféle stílusban is lehet használni, a fentiek mindegyikét tudja többé kevésbé
  • sokszor vegyesen is használjuk
  • erősen támogatja az objektumorientált paradigmát. Minden objektum!
In [ ]:
import random
def foo():
    pass
[int,bool,foo,random] 

Objektumorientált programozás

A programot objektumok köré szervezzük. Minden objektum tartalmaz adatokat és metódusokat, amik az adatokat kezelik.

Osztály (class) definiálása

  • class kulcsszóval
  • példányokat tudunk létrehozni
  • minden példánynak van egy saját namespace
  • attribútumokat tudunk kapcsolni hozzájuk melyek nem befolyásolják a többi példány attribútumait
In [ ]:
class Ember:
    pass
In [ ]:
a=Ember()
a.nev="Gipsz Jakab"
a.kor=24
b=Ember()
b.nev="Gipsz Jakabné"
b.kor=22
l=[a,b]
for e in l:
    print(e.nev,e.kor)

Init

  • __init__ függvény automatikusan meghívódik amikor a példány elkészül.
    • olyasmi mint egy konstruktor
    • nem kötelező
  • minden függvény első paramétere maga a példány. Ezt megszokásból self-nek hívjuk.
In [ ]:
class Ember:
    def __init__(self,nev,kor):
        print("Létrejött egy ember")
        self.kor = kor
        self.nev = nev
 
In [ ]:
a=Ember("Gipsz Jakab",24)
b=Ember("Gipsz Jakabné",22)

l=[a,b]
for e in l:
    print(e.nev,e.kor)

Metódusok

  • Függvények az osztály definíciójában
  • Automatikusan az első argumentumuk a példány lesz.
In [ ]:
class Ember:
    def __init__(self,nev,kor):
        self.kor = kor
        self.nev = nev
        
    def szuletesi_ev(self):    # egy paramétert vár
        print(2020-self.kor)
        
    def egykoru(self,other):    
        print(self.kor==other.kor)
 
    
a=Ember("Gipsz Jakab",24)
a.szuletesi_ev()                # de paraméter nélkül hívjuk meg, mivel az első paraméter maga 'a' lesz  
b=Ember("Gipsz Jakabné",22)
a.egykoru(b)

Metódusok meghívása

Két lehetőség van:

  1. példány.metódus(param)
  2. class.metódus(példány, param)
In [ ]:
a=Ember("Gipsz Jakab",24)
a.szuletesi_ev()                
Ember.szuletesi_ev(a)

__str__ metódus

Egy speciális metódus, amit arra használunk hogy megadjuk, hogy a print() függvény hogyan írja ki az objektumot.

In [ ]:
class Ember:
    def __init__(self,nev,kor,lakohely):
        self.kor = kor
        self.nev = nev
        self.lakohely = lakohely
    def __str__(self): 
        return self.nev+" egy "+str(self.kor)+" éves "+ self.lakohely + "i lakos."
    
a=Ember("Gipsz Jakab",24,"budapest")
print(a)
b=Ember("Gipsz Jakabné",22,"kecskemét")
print(b)

Osztály attribútumok

  • Olyan attribútum, amin az ossztály összes tagja osztozik.
In [ ]:
class Ember:
    letszam = 42

A példányokon és a class objektumon keresztül is elérjük

In [ ]:
a = Ember()
a.letszam
In [ ]:
Ember.letszam

Megváltoztatni a classon keresztül lehet

In [ ]:
a1 = Ember()
a2 = Ember()

print(a1.letszam,a2.letszam)
Ember.letszam = 43
a1.letszam, a2.letszam

A példányokon keresztül viszont nem

In [ ]:
a1 = Ember()
a2 = Ember()

a1.letszam = 11
a2.letszam

Azért, mert ez egy új attribútumot hoz létre a példány namespacében.

In [ ]:
a1.letszam

Öröklődés

In [ ]:
class Ember:
    pass

class Matematikus(Ember):
    pass

e = Ember()
m = Matematikus()
print(isinstance(e, Matematikus))
print(isinstance(m, Ember))
print(issubclass(Ember, Matematikus))
print(issubclass(Matematikus,Ember))

Ha nem írunk semmilyen osztályt, automatikusan az object osztály a szülő osztály:

In [ ]:
class A: pass
class B(object): pass

print(issubclass(A, object))
print(issubclass(B, object))

Metódus öröklődés

A metódusok öröklődnek, de felülírhatóak.

In [ ]:
class A(object):
    def foo(self):
        print("A.foo függvény")
        
    def bar(self):
        print("A.bar függvény")
        
class B(A):
    def foo(self):
        print("B.foo függvény")
        
b = B()
b.foo()
b.bar()

Mivel az adat attribútumok bárhol létrehozhatóak, csak akkor öröklődnek, ha a szülő osztályban lévő kód meghívódik.

In [ ]:
class A(object):
    
    def foo(self):
        self.value = 42
        
class B(A):
    pass

b = B()
print(b.__dict__)            # a __dict__ kiírja az összes attribútumot 
a = A()
print(a.__dict__)
a.foo()
print(a.__dict__)
print(b.__dict__)
b.foo()
print(b.__dict__)

A szülő osztály konstruktora

  • meghívódik az szülő osztály __init__ függvénye. Viszont mivel az __init__ nem egy szokásos konstruktor, így nem hívódik meg automatikusan a szülő osztály init függvénye, ha felülírja a gyerek osztály.
In [ ]:
class A(object):
    def __init__(self):
        print("A.__init__ called")
        
class B(A):
    #pass
    def __init__(self):
        print("B.__init__ called")
        
class C(A):
    pass
        
b = B()
print("c létrehozása")
c = C()

A szülő osztály metódusai kétféleképpen is elérhetőek

  1. a már tanult módon, az osztály nevével
  2. vagy pedig a super függvény segítségével
In [ ]:
class A(object):
    def __init__(self):
        print("A.__init__ ")
        
        
class B(A):
    def __init__(self):
        A.__init__(self)
        print("B.__init__ ")
        
class C(A):
    def __init__(self):
        super().__init__()
        print("C.__init__ ")
        
print("B")
b = B()
print("C")
c = C()
In [ ]:
class A(object):
    def __init__(self):
        print("A.__init__ ")
        
        
class B(A):
    def __init__(self):
        print("B.__init__ ")
        
class C(B):
    def __init__(self):
        super().__init__()
        print("C.__init__ ")
        
c = C()
In [ ]:
class Ember(object):
    
    def __init__(self, nev, kor):
        self.nev = nev
        self.kor = kor
        
    def __str__(self):
        return "{0}, életkor: {1}".format(self.nev, self.kor)
        
class Alkalmazott(Ember):
    
    def __init__(self, nev, kor, pozicio, fizetes):
        self.pozicio = pozicio
        self.fizetes = fizetes
        super().__init__(nev, kor)
        
    def __str__(self):
        return "{0}, pozíció: {1}, fizetés: {2}".format(super().__str__(), self.pozicio, self.fizetes)
    
    
e = Alkalmazott("Jakab Gipsz", 33, "főnök", 450000)
print(e)
print(Ember(e.nev, e.kor))

1. Példa: polinomok

In [ ]:
class Polinom:
    
    def __init__(self, lista):
        self.ehlista=lista
       
    def __str__(self):
        szoveg=""
        for i,eh in enumerate(self.ehlista):
            szoveg=szoveg+str(eh)+"x^"+str(i)+"+"
        szoveg=szoveg.rstrip("+")    
        return szoveg
    
    def deri(self):
        l=[]
        for i,eh in enumerate(self.ehlista):
            if i==0:
                pass
            else:
                l.append(i*eh)
        return Polinom(l)
In [ ]:
a=Polinom([1,2,4,5])
print(a)
print(a.deri())
In [ ]:
class Masodfoku(Polinom):
    def egyikgyok(self):
        a=self.ehlista[2]
        b=self.ehlista[1]
        c=self.ehlista[0]
        return (-b+(b**2-4*a*c)**(1/2))/(2*a) 
In [ ]:
p=Masodfoku([2,3,1])
print(p)
p.egyikgyok()
In [ ]:
class Polinom:
    
    def __init__(self, lista):
        self.ehlista=lista
       
    def print_it(self):
        szoveg=""
        for i,eh in enumerate(self.ehlista):
            szoveg=szoveg+str(eh)+"x^"+str(i)+"+"
        szoveg=szoveg.rstrip("+")    
        print(szoveg)
    
    def deri(self):
        l=[]
        for i,eh in enumerate(self.ehlista):
            if i==0:
                pass
            else:
                l.append(i*eh)
        return Polinom(l)
    
    def beh(self,x):
        valasz=0
        for i,eh in enumerate(self.ehlista):
            valasz=valasz+eh*pow(x,i)
        return valasz
class Masodfoku(Polinom):
    def egyikgyok(self):
        a=self.ehlista[2]
        b=self.ehlista[1]
        c=self.ehlista[0]
        return (-b+(b**2-4*a*c)**(1/2))/(2*a) 
p=Polinom([2,1])
print(p.beh(5))
m=Masodfoku([1,2,1])
m.beh(m.egyikgyok())

2. Példa: Sárkányok

sarkany

In [ ]:
class sarkany:
    def __init__(self, nev):
        self.nev=nev
In [ ]:
class repulo_sarkany(sarkany):
    def __init__(self, nev, szarnyfesztav):
        super().__init__(nev)
        self.szarnyfesztav=szarnyfesztav
    
    def tamadas(self):
        print("A", self.nev, "nevű sárkány a levegőből rád vetette magát", )
        
    def repules(self):
        print("A", self.nev, "nevű sárkány a repül", )
        
In [ ]:
smaug=repulo_sarkany("Smaug",12)
smaug.repules()
smaug.tamadas()
In [ ]:
class tuzokado_sarkany(sarkany):
    def __init__(self, nev):
        super().__init__(nev)
    
    def tamadas(self):
        print("A", self.nev, "nevű sárkány szénné égetett", )
        
    def tuzokadas(self):
        print("A", self.nev, "nevű sárkány a tüzet okád", )
    
In [ ]:
susu=tuzokado_sarkany("Süsü")
susu.tuzokadas()
susu.tamadas()

Többszörös öröklődés

In [ ]:
class repulo_tuzokado_sarkany(repulo_sarkany,tuzokado_sarkany):
     def __init__(self, nev, szarnyfesztav):
        self.nev=nev  
        self.szarnyfesztav=szarnyfesztav
        

Vajon mi lesz az eredmény?

In [ ]:
viserion=repulo_tuzokado_sarkany("Viserion",25)
viserion.repules()
viserion.tuzokadas()
viserion.tamadas()

Azt, hogy melyik hívódik meg, az MRO (Method Resolution Order) határozza meg.

In [ ]: