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í:
class Student:
def __init__(self, name, points):
self.name = name
self.points = points
john = Student('John', 10)
print(john.name) # John
john.points += 20
print(john.points) # 30
(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
.
class Student:
def __init__(self, name):
self.name = name
self.points = 0
def add_points(self, new_points):
"""Pridani bodu studentovi.
"""
self.points += new_points
def __str__(self):
"""Vraci informacni retezec. (Viz 'print(student'.))
"""
return '<Student %s, points: %s>' % (self.name, self.points)
john = Student('John')
print(john)
john.add_points(20)
print(john)
(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()
.
from turtle import Turtle
turtle1 = Turtle()
turtle2 = Turtle()
turtle1.right(30)
turtle1.forward(100)
turtle2.right(150)
turtle2.forward(200)
(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.
class Circle:
def __init__(self, x, y, radius, color):
pass
circle = Circle(4, 5, 10, 'red')
print(circle.y) # 5
print(circle.color) # red
(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.
class Rectangle:
pass
def area(rectangle):
pass
rectangle = Rectangle(4, 3)
print(rectangle.width) # 4
print(rectangle.height) # 3
print(area(rectangle)) # 12
(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ů.
class Point:
pass
def distance(point1, point2):
pass
point = Point(4, 5)
print(point.x) # 4
print(point.y) # 5
point.x = 10
print(point.x) # 10
print(distance(Point(1, 2), Point(3, 5))) # 3.6055
print(distance(Point(1, 4), Point(2, 3))) # 1.4142
(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.
class Book:
pass
def print_info(book):
pass
def draw_cover(book):
pass
cookbook = Book('Cooking for Geeks', 'Jeff Potter', '0596805888', 22)
print_info(cookbook)
draw_cover(cookbook)
(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í.
class Turtle:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 0
self.lines = []
def left(self, angle):
self.direction -= angle
def right(self, angle):
self.direction += angle
def forward(self, distance):
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):
svg = ""
for x1, y1, x2, y2 in self.lines:
svg += """\t<line x1='{}' y1='{}' x2='{}' y2='{}'
style='stroke: black; stroke-width:2' />\n""".format(x1, y1, x2, y2)
return svg
def save(self, filename):
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>")
(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 metodusave()
), 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 typufind_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.
class Creature:
def __init__(self, name, level, health_max):
"""Inicialize atributu objektu
"""
pass
def __str__(self):
"""Vraci textovou reprezentaci prisery.
"""
pass
def get_level(self):
"""Vraci level prisery
"""
pass
def new_level(self):
"""Zvysi level o 1
"""
pass
def attack(self, enemy):
"""Provede utok na protivnika.
Vysledek zavisi na rozdilu meho a protivnikova levelu.
"""
pass
def undertake_attack(self, damage):
"""Podstoupi utok o dane sile.
Ubere si prislusny pocet zivotnich bodu.
"""
pass
(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.
class Stack:
pass
# ukazka fungovani
stack = Stack()
stack.push(5)
stack.push(10)
print(stack.pop()) # 10
print(stack.is_empty()) # False
(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.
class Queue:
pass
# ukazka fungovani
queue = Queue()
queue.append(5)
queue.append(10)
print(queue.pop()) # 5
(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
).
from math import pi
# Implementujte tridy Square, Circle a Triangle
square1 = Square((10, 20), 100)
square2 = Square((0, 0), 15)
circle1 = Circle((-130, -130), 100)
for shape in [square1, circle1, square2]:
print(shape)
print('perimeter:', shape.get_perimeter())
print('---')
(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
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __add__(self, vector):
"""
Vraci vektor, ktery je souctem vektoru 'self' a 'vector'
"""
pass # TODO
def __rmul__(self, c):
"""
Vraci vektor 'self' vynasobeny cislem 'c'
"""
pass # TODO
u = Vector(1, 3)
v = Vector(2, 7)
print(u + v) # (3, 10)
print(3 * u) # (3, 9)
(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ě.
class Grid:
def __init__(self, size):
"""
Inicializuje prazdnou mrizku.
"""
pass
def in_grid(self, coords):
"""
Vraci True, pokud zadany bod (x, y) lezi v hracim poli
"""
pass
def add_symbol(self, symbol, coords):
"""
Prida dany symbol ('X' nebo 'Y') do mrizky
(pokud jde o platnou a prazdou pozici).
"""
pass
def __str__(self):
"""
Vraci textovou reprezentaci mrizky.
"""
pass
def check_five(self):
"""
Vraci True, pokud v poli existuje petice stejnych symbolu v rade
(vertikalne / horizontalne / diagonalne).
"""
pass
def generate_move(self, symbol='X'):
"""
Zkusi zahrat nasledujici tah (nahodne / tak, aby nenahraval na vyhru
a sam vyhral, pokud muze).
"""
pass
def is_full(self):
"""
Vraci True, pokud je mrizka zcela zaplnena.
"""
pass
(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.)