9. 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í:
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
.
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()
.
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
).
9.1. Struktury¶
9.1.1. Barevné kruhy¶
Vytvořte třídu pro reprezentaci barevného kruhu na daných souřadnicích.
class Circle:
def __init__(self, x: float, y: float, radius: float, color: str) -> None:
pass
def test_circle() -> None:
circle = Circle(4, 5, 10, 'red')
assert circle.y == 5
assert circle.color == 'red'
9.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.
class Rectangle:
pass
def area(rectangle: Rectangle) -> float:
pass
def test_rectangle() -> None:
rectangle = Rectangle(4, 3)
assert rectangle.width == 4
assert rectangle.height == 3
assert area(rectangle) == 12
9.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ů.
9.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.
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 |
| |
| |
| |
| |
+-------------------------+
9.2. Objekty s metodami¶
9.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__
).
class Circle:
def __init__(self, center: float, radius: float) -> None:
pass
def get_perimeter(self) -> float:
pass
def get_area(self) -> float:
pass
def __str__(self) -> str:
pass
def test_circle() -> None:
circle = Circle((-130, -130), 100)
assert str(circle) == "Circle at (-130, -130) with radius 100"
assert circle.get_area() == 31415.926535897932
9.2.2. Želvy¶
Prozkoumejte následující třídu pro reprezentaci kreslící želvy a vyzkoušejte si její použití.
class Turtle:
def __init__(self) -> None:
self.x = 0
self.y = 0
self.direction = 0
self.lines = []
def left(self, angle: float) -> None:
self.direction -= angle
def right(self, angle: float) -> None:
self.direction += angle
def forward(self, distance: float) -> None:
nx = self.x + distance * math.cos(self.direction * math.pi / 180)
ny = self.y + distance * math.sin(self.direction * math.pi / 180)
self.lines.append((self.x, self.y, nx, ny))
self.x, self.y = nx, ny
def get_lines_svg(self) -> str:
svg = ""
for x1, y1, x2, y2 in self.lines:
svg += """\t<line x1='{}' y1='{}' x2='{}' y2='{}'
style='stroke: red; stroke-width:2' />\n""".format(x1, y1, x2, y2)
return svg
def save(self, filename: str) -> None:
with open(filename, "w") as f:
f.write('<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">\n')
f.write(self.get_lines_svg())
f.write("\n</svg>")
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: str) -> None
).Přidejte želvě metodu, která ji posune o danou vzdálenost náhodným směrem (
random_step(self, distance: float) -> None
).Napište funkci
chase(steps: int = 10) -> None
. 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řeba o 100). Nakonec funkce uloží obě želvy do jednoho svg (pozn.: není možné přímo použít metodusave()
), můžete využít metodu:def turn_towards(self, other_turtle: 'Turtle') -> None: self.direction = math.atan2(other_turtle.y - self.y, other_turtle.x - self.x) / math.pi * 180
Napište funkci
multichase(steps: int = 10, turtles: int = 10) -> None
, 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 typufind_nearest(self, turtles: List['Turtle']) -> 'Turtle'
, která vrátí nejbližší želvu z daného seznamu).Zkuste změnit želvy na tanky, které po sobě budou střílet.
9.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.
9.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.
class Stack:
pass
def test_stack() -> None:
stack = Stack()
stack.push(5)
stack.push(10)
assert stack.pop() == 10
assert not stack.is_empty()
assert stack.pop() == 5
assert stack.is_empty()
9.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.
class Queue:
pass
def test_queue() -> None:
queue = Queue()
queue.enqueue(42)
queue.enqueue(10)
queue.enqueue(5)
assert not queue.is_empty()
assert queue.dequeue() == 42
queue.sort()
stack = queue.to_stack()
assert stack.pop() == 10
assert queue.dequeue() == 5
9.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.
9.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
).
9.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
class Vector:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
def __str__(self) -> str:
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __add__(self, vector: 'Vector') -> 'Vector':
"""Return vector 'self'+'vector'."""
pass # TODO
def __rmul__(self, c: float) -> 'Vector':
"""Return vector 'self'*c."""
pass # TODO
def test_vector() -> None:
u = Vector(1, 3)
v = Vector(2, 7)
w = u + v
assert w.x == 3
assert w.y == 10
w = 3 * u
assert w.x == 3
assert w.y == 9
9.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ě.
class Grid:
def __init__(self, size: int) -> None:
pass
def in_grid(self, coords: Tuple[int, int]) -> bool:
"""Return true iff corrds=(x,y) is in the grid."""
pass
def add_symbol(self, symbol: str, coords: Tuple[int, int]) -> bool:
"""
Add symbol 'symbols' to grid at position 'coords' (if the coordine
is valid).
Returns True if successful, False otherwise.
"""
pass
def __str__(self) -> str:
pass
def check_five(self) -> bool:
"""Return true iff there are 5 same symbols in a row in a grid
(vertically or horizonally or diagonally).
"""
pass
def generate_move(self, symbol: str = 'X') -> Optional[Tuple[int, int]]:
"""
Generates random move so the move does not help an enemy to win
and self player wins, if currennly possible.
"""
pass
def is_full(self) -> bool:
"""Return true iff the contains no empty fields."""
pass
9.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.)