10. Objekty

Objekty slouží ke spojení souvisejících dat a často i operací na těchto datech. Třída (class) je předpis (šablona) pro vytváření nových objektů nějakého typu. Nejjednodušším případem jsou tzv. struktury (záznamy), sloužící pouze ke spojení souvisejících dat (např. souřadnic jednoho bodu). Některé programovací jazyky mají speciální typ pro definici struktury obsahující několik datových položek, v Pythonu se k tomu využívají jednoduché třídy s metodu __init__, která nastaví (inicializuje) všechny požadované atributy struktury. Syntaxe pro vytvoření struktury je následující:

 
1
class Student:
2
    def __init__(self, name, points):
3
        self.name = name
4
        self.points = points
5
6
john = Student('John', 10)
7
print(john.name)    # John
8
john.points += 20
9
print(john.points)  # 30
10

(student_structure_demo)

Třída může obsahovat i další metody (funkce pracující na datech objektu). Objekt pak má nějaký stav (data, atributy) a navíc i chování (metody). Prvním parametrem metody je vždy objekt, na kterém došlo k volání této metody, silnou konvencí je nazývat tento parametr self.

20
 
1
class Student:
2
    def __init__(self, name):
3
        self.name = name
4
        self.points = 0
5
6
    def add_points(self, new_points):
7
        """Pridani bodu studentovi.
8
        """
9
        self.points += new_points
10
11
    def __str__(self):
12
        """Vraci informacni retezec. (Viz 'print(student'.))
13
        """
14
        return '<Student %s, points: %s>' % (self.name, self.points)
15
16
john = Student('John')
17
print(john)
18
john.add_points(20)
19
print(john)
20

(student_class_demo)

S objekty jsme pracovali již mnohokrát. Například seznamy jsou objekty s metodami jako append, sort, atd. A třeba knihovna turtle obsahuje třídu Turtle pro reprezentaci kreslící želvy. Nový objekt želvy se vytvoří voláním turtle1 = Turtle().

9
 
1
from turtle import Turtle
2
3
turtle1 = Turtle()
4
turtle2 = Turtle()
5
turtle1.right(30)
6
turtle1.forward(100)
7
turtle2.right(150)
8
turtle2.forward(200)
9

(turtles_demo)

Další konvencí Pythonu je pojmenovávat atributy, ke kterým by nemělo být přistupováno z vnějšku přímo, ale pouze pomocí metod (tzv. soukromé atributy), s podtržítkem na začátku (např. _name).

10.1. Struktury

10.1.1. Barevné kruhy

Vytvořte třídu pro reprezentaci barevného kruhu na daných souřadnicích.

9
 
1
class Circle:
2
3
    def __init__(self, x, y, radius, color):
4
        pass
5
6
circle = Circle(4, 5, 10, 'red')
7
print(circle.y)       # 5
8
print(circle.color)   # red
9

(colored_circles_task)

10.1.2. Obdélník

Napište třídu Rectangle pro reprezentaci obdélníku o dané šířce a výšce a funkci pro výpočet obsahu předaného obdélníku.

11
 
1
class Rectangle:
2
    pass
3
4
def area(rectangle):
5
    pass
6
7
rectangle = Rectangle(4, 3)
8
print(rectangle.width)   # 4
9
print(rectangle.height)  # 3
10
print(area(rectangle))   # 12
11

(rectangle_task)

10.1.3. Bod

Napište třídu pro reprezentaci bodu v rovině (parametry x a y). Potom implementujte funkci, která vypočítá vzdálenost dvou bodů.

14
 
1
class Point:
2
    pass
3
4
def distance(point1, point2):
5
    pass
6
7
point = Point(4, 5)
8
print(point.x)       # 4
9
print(point.y)       # 5
10
point.x = 10
11
print(point.x)       # 10
12
print(distance(Point(1, 2), Point(3, 5)))   # 3.6055
13
print(distance(Point(1, 4), Point(2, 3)))   # 1.4142
14

(point_task)

10.1.4. Kniha

Napište třídu pro reprezentaci knihy (s atributy název, autor, ISBN a cena). Dále napište funkce print_info pro výpis informací o knize a draw_cover pro vykreslení obálky knihy.

13
 
1
class Book:
2
    pass
3
4
def print_info(book):
5
    pass
6
7
def draw_cover(book):
8
    pass
9
10
cookbook = Book('Cooking for Geeks', 'Jeff Potter', '0596805888', 22)
11
print_info(cookbook)
12
draw_cover(cookbook)
13

(book_task)

Výstup může vypadat třeba nějak takto (nemusíte si ale hrát se zarovnáním do středu):

name:   Cooking for Geeks
author: Jeff Potter
isbn:   0596805888
price:  $22

+-------------------------+
|                         |
|                         |
|                         |
|    Cooking for Geeks    |
|                         |
|                         |
|                         |
|       Jeff Potter       |
|                         |
|                         |
|                         |
|                         |
+-------------------------+

10.2. Objekty s metodami

10.2.1. Kruh

Napište třídu pro reprezentaci kruhu s daným středem a poloměrem. Implementujte metody pro výpočet obvodu, obsahu a vrácení informačního řetězce (__str__).

10.2.2. Želvy

Prozkoumejte následující třídu pro reprezentaci kreslící želvy a vyzkoušejte si její použití.

33
 
1
class Turtle:
2
    def __init__(self):
3
        self.x = 0
4
        self.y = 0
5
        self.direction = 0
6
        self.lines = []
7
8
    def left(self, angle):
9
        self.direction -= angle
10
11
    def right(self, angle):
12
        self.direction += angle
13
14
    def forward(self, distance):
15
        nx = self.x + distance * math.cos(self.direction * math.pi / 180)
16
        ny = self.y + distance * math.sin(self.direction * math.pi / 180)
17
18
        self.lines.append((self.x, self.y, nx, ny))
19
        self.x, self.y = nx, ny
20
21
    def get_lines_svg(self):
22
        svg = ""
23
        for x1, y1, x2, y2 in self.lines:
24
            svg += """\t<line x1='{}' y1='{}' x2='{}' y2='{}'
25
                style='stroke: black; stroke-width:2' />\n""".format(x1, y1, x2, y2)
26
        return svg
27
28
    def save(self, filename):
29
        with open(filename, "w") as f:
30
            f.write('<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">\n')
31
            f.write(self.get_lines_svg())
32
            f.write("\n</svg>")
33

(turtles_task)

  • Přidejte želvě metodu pro vykreslení mnohoúhelníka.

  • Upravte želvu tak, aby začínala na náhodné pozici (třeba v intervalu 0-500).

  • Upravte želvu tak, aby uměla kreslit i jinou barvou než černou (např. můžete přidat metodu set_color(self, color)).

  • Přidejte želvě metodu, která ji posune o danou vzdálenost náhodným směrem (random_step(self, distance)).

  • Napište funkci chase(steps=10). Funkce vytvoří 2 želvy, jedna bude honit druhou. První (černá) bude daný počet kroků náhodně chodit (třeba o 100), druhá (červená) bude vždy chodit směrem k první (taky třena o 100).Nakonec funkce uloží obě želvy do jednoho svg (pozn.: není možné přímo použít metodu save()), můžete využít metodu:

    def turn_towards(self, other_turtle):
        self.direction = math.atan2(other_turtle.y - self.y,
                            other_turtle.x - self.x) / math.pi * 180
    
  • Napište funkci multichase(steps=10, turtles=10), která vytvoří požadovaný počet želv a uloží je do seznamu; v každém kroku každá ze želv udělá krok (třeba 10) k nejbližší želvě (může se hodit metoda typu find_nearest(self, turtles), která vrátí nejbližší želvu z daného seznamu).

  • Zkuste změnit želvy na tanky, které po sobě budou střílet.

10.2.3. Příšera

Doplňte následující kostru třídy a vyzkoušejte její funkčnost. Poté přidejte příšeře další metody, např. is_defeated, která vrací True, pokud už příšera nemá žádné životy.

36
 
1
class Creature:
2
3
    def __init__(self, name, level, health_max):
4
        """Inicialize atributu objektu
5
        """
6
        pass
7
8
    def __str__(self):
9
        """Vraci textovou reprezentaci prisery.
10
        """
11
        pass
12
13
    def get_level(self):
14
        """Vraci level prisery
15
        """
16
        pass
17
18
    def new_level(self):
19
        """Zvysi level o 1
20
        """
21
        pass
22
23
    def attack(self, enemy):
24
        """Provede utok na protivnika.
25
26
        Vysledek zavisi na rozdilu meho a protivnikova levelu.
27
        """
28
        pass
29
30
    def undertake_attack(self, damage):
31
        """Podstoupi utok o dane sile.
32
33
        Ubere si prislusny pocet zivotnich bodu.
34
        """
35
        pass
36

(monster_task)

10.2.4. Zásobník

Vytvořte třídu pro reprezentaci zásobníku podporující operace přidání prvku, odebrání prvku a test na prázdnost.

10
 
1
class Stack:
2
    pass
3
4
# ukazka fungovani
5
stack = Stack()
6
stack.push(5)
7
stack.push(10)
8
print(stack.pop())       # 10
9
print(stack.is_empty())  # False
10

(stack_task)

10.2.5. Fronta

Vytvořte třídu pro reprezentaci fronty podporující operace přidání prvku, odebrání prvku, test na prázdnost, seřazení prvků ve frontě a konverzi fronty na zásobník.

9
 
1
class Queue:
2
    pass
3
4
# ukazka fungovani
5
queue = Queue()
6
queue.append(5)
7
queue.append(10)
8
print(queue.pop())  # 5
9

(queue_task)

10.2.6. Knihovna

Vytvořte třídu Library pro reprezentaci kolekce knih s metodami pro přidání knihy, odebrání knihy, nalezení knihy podle názvu nebo ISBN, nalezení všech knih daného autora, nalezení všech knih s cenu pod zadanou mez. Pro reprezentaci knih využijte třídu Book ze cvičení Kniha.

10.2.7. Geometrické tvary

Napište třídy pro několik jednoduchých geometrických útvarů (čtverec, kruh, rovnostranný trojúhelník). Každá třída bude mít metody pro výpočet obvodu, výpočet obsahu a vrácení informačního řetězce (__str__). V následující ukázce si všimněte dynamické (pozdní) vazby jmen metod (např. get_perimeter na řádku 10) na jejich definice (tj. skutečně volaná metoda se různí podle toho, na jaký objekt zrovna ukazuje proměnná shape).

12
 
1
from math import pi
2
3
# Implementujte tridy Square, Circle a Triangle
4
5
square1 = Square((10, 20), 100)
6
square2 = Square((0, 0), 15)
7
circle1 = Circle((-130, -130), 100)
8
for shape in [square1, circle1, square2]:
9
    print(shape)
10
    print('perimeter:', shape.get_perimeter())
11
    print('---')
12

(shapes_task)

10.2.8. Vektory

Python umožňuje přetížit mnoho operátorů (např. +, * atp.) skrze tzv. magické funkce (např. __add__ pro přetížení operátoru +). S využitím následující kostry implementujte třídu reprezentující vektor

25
 
1
class Vector:
2
    def __init__(self, x, y):
3
        self.x = x
4
        self.y = y
5
6
    def __str__(self):
7
        return "(" + str(self.x) + ", " + str(self.y) + ")"
8
9
    def __add__(self, vector):
10
        """
11
        Vraci vektor, ktery je souctem vektoru 'self' a 'vector'
12
        """
13
        pass  # TODO
14
15
    def __rmul__(self, c):
16
        """
17
        Vraci vektor 'self' vynasobeny cislem 'c'
18
        """
19
        pass  # TODO
20
21
u = Vector(1, 3)
22
v = Vector(2, 7)
23
print(u + v)  # (3, 10)
24
print(3 * u)  # (3, 9)
25

(vector_task)

10.2.9. Piškvorky

Vytvořte třídu Grid pro reprezentaci čtverečkovaného pole s rozehranou partií piškvorek. Využijte pak tuto třídu pro hru piškvorek mezi 2 hráči (střídají se 2 hráči (symboly ‘X’ a ‘O’), tah (poloha symbolu) se načítá jako vstup od uživatele, vyhrává hráč, který má 5 svých znaků v řadě.

47
 
1
class Grid:
2
3
    def __init__(self, size):
4
        """
5
        Inicializuje prazdnou mrizku.
6
        """
7
        pass
8
9
    def in_grid(self, coords):
10
        """
11
        Vraci True, pokud zadany bod (x, y) lezi v hracim poli
12
        """
13
        pass
14
15
    def add_symbol(self, symbol, coords):
16
        """
17
        Prida dany symbol ('X' nebo 'Y') do mrizky
18
        (pokud jde o platnou a prazdou pozici).
19
        """
20
        pass
21
22
    def __str__(self):
23
        """
24
        Vraci textovou reprezentaci mrizky.
25
        """
26
        pass
27
28
    def check_five(self):
29
        """
30
        Vraci True, pokud v poli existuje petice stejnych symbolu v rade
31
        (vertikalne / horizontalne / diagonalne).
32
        """
33
        pass
34
35
    def generate_move(self, symbol='X'):
36
        """
37
        Zkusi zahrat nasledujici tah (nahodne / tak, aby nenahraval na vyhru
38
        a sam vyhral, pokud muze).
39
        """
40
        pass
41
42
    def is_full(self):
43
        """
44
        Vraci True, pokud je mrizka zcela zaplnena.
45
        """
46
        pass
47

(piskvorky_task)

10.3. Doplňující zdroje

(Námět: napište funkci, která dostane seznam (barevných kruhů) a nějakým způsobem je vykresli – např. pomocí želví grafiky, knihovny Image, nebo uložením vektorového obrázku ve formátu SVG.)

Next Section - 11. Práce s textem