previous up next hi end end

12. Třídy a instance

  1. Objektově orientované programování
  2. Vytvoření třídy
  3. Atributy
  4. Inicializační metoda   'init'
  5. Obdélník
  6. Instance jsou měnitelné
  7. Instance jako výstupní hodnoty
  8. Instance uvnitř f-stringu
  9. Kopírování třídy
  10. Dekorátory II
  11. Speciální metody a atributy
  12. Datové třídy
  13. Dědění
  14. Propojení tříd
  15. Glosář
  16. Cvičení

12.1 Objektově orientované programování

Python je programovací jazyk, který podporuje objektově orientované programování (OOP).

Při objektově orientovaném programování je pozornost zaměřena na vytváření objektů, které obsahují jak data, tak funkce či metody.

Téměř vše v Pythonu je objekt. Objekt je kolekce dat (proměnných) a metod (funkcí), které s danými daty pracují. Prototypem objektů jsou třídy, z nichž jsou všechny objekty (čísla, řetězce, funkce, moduly, metody, atp) odvozeny coby instance.


12.2 Vytvoření třídy

Třída je logické seskupení dat a funkcí. Ve své podstatě definuje nový, složený datový typ. Různé vestavěné typy - třídy Pythonu (str, int, float, ...) používáme již od počátečních kapitol.

Nejjednodušší definice třídy vypadá takto:

class Emanuel:
    '''Nepovinný dokumentační řetězec - docstring '''
    pass

Záhlaví složené z klíčového slova class, názvu třídy a dvojtečky je vše, co jednoduchá třída potřebuje.
Později se dovíme, že třída může dědit ze své nadtřídy, v tom případě by před dvojtečkou byl název supertřídy v závorkách - class Emanuel(superClass): .
Následuje odsazené tělo třídy, které bude obsahovat řadu dalších objektů. V naší demonstraci jsme uvedli příklad dokumentačního řetězce a jedno klíčové slovo pass, které nedělá nic. Pro platnost minimální definice postačí jedno z uvedeného.
S naší prostinkou třídou můžeme už provádět řadu regulérních úkonů, například:

Definice třídy se mohou vyskytnout kdekoli v programu, ale obvykle se umisťují poblíž počátku (po příkazech import).

Pro zvídavé:
Třídy samotné jsou instancemi prvotní třídy type (termín type je synonymem termínu class):

>>> x = [2, 5, 8], y = "Nazdar"
# x, y jsou instancemi tříd list a str:
>>> type(x), type(y)
(<class 'list'>, <class 'str'>)
# list a str jsou instancemi třídy type:
>>> type(type(x)), type(type(y))            
(<class 'type'>, <class 'type'>)
# tudíž nepřekvapí že:
>>> type(Point)
<class 'type'>

Nebudeme ale předbihat. Konstrukci třídy si krok po kroku vysvětlíme na konkretním případě třídy Point, kterou si vytvoříme pro manipulaci s bodem ve dvourozměrném prostoru.

Třída Point

Polohu bodu v rovině určíme jeho souřadnicemi. V matematickém zápisu bodu se tyto souřadnice, oddělené čárkou, uvádějí v závorce. Například, (0, 0) představuje počátek souřadnic, (x, y) představuje bod ve vzdálenosti x ve vodorovném a y ve svislém směru od počátku.

class Point: 
    pass

Vytvořením třídy vytvoříme prototyp možných konkrétních objektů, neboli instancí  třídy. Vytvoření nové instance připomíná volání funkce:

>>> p = Point()             # vytvoření objektu (instance třídy)

Ke třídě Point se vrátíme v odstavci 12.4.


12.3 Atributy

Slovem atribut označujeme obecně vlastnosti nějakého objektu, v tomto případě jím označujeme vlastnosti třídy a instance. Atributy třídy i instance jsou dvojího druhu - datové a funkční.

Datovými atributy jsou proměnné, funkčními atributy jsou metody, což jsou funkce, definované uvnitř třídy a volané pro instanci (objekt) tečkovou notací. Atributem je také případný dokumentační řetězec.

Datové atributy

Pro instanci i třídu lze určit atributy i dodatečně (jak jsme již viděli v předběžné ukázce):

>>> p.x = 3                # datový atribut 'x' instance 'p'  
>>> p.y = 4                # datový atribut 'y' instance 'p' 

>>> Point.a = "alef"       # datový atribut 'a' třídy Point

Tato skladba je podobná skladbě pro výběr atributu z modulu, např. math.pi nebo string.ascii_uppercase. Jak moduly, tak i třídy a instance vytvářejí své vlastní jmenné prostory. Přístup k jejich jménům (atributům) je v obou případech stejný - přes tečkovou notaci. V naší ukázce vytváříme atributy tím, že jim přiřazujeme hodnoty.

Ověřme si to:

>>> p.x, p.y
(3,4)
>>> Point.a
'alef'

Tečkovou notaci můžeme použít jako součást jakéhokoliv výrazu, takže následující příkazy jsou legální:

>>> print('%d  %d' % (p.x, p.y))
3 4
>>> print(p.x*p.x + p.y*p.y)
25

Stejně snadné jako vytvoření objektu a deklarace jeho atributů (případně atributů třídy), je jejich smazání příkazem del:

>>> del p.y
>>> del Point.a
>>> del p

Metody

Pro třídu v Pythonu se rozlišují tři metody: instanční, statická a metoda třídy:

  1. Instanční metoda definuje chování objektu (instance), nikoli třídy. Definice obsahuje parametr self. V odstavci 12.4 uvedená speciální metoda __init__ je eminentní metoda instanční.
    class Sample:                         deklarace třídy
        val = 10                          datový atribut třídy
        def myFunc(self):                 metoda instance
            print("Value: ", self.val)
    
    >>> obj = Sample()                    vytvoření instance
    >>> obj.myFunc()                      volání metody
    Value:  10
    
  2. Metoda třídy definuje chování celé třídy, nikoli objektu. Definice obsahuje parametr cls a je uvedena vestavěným dekorátorem @classmethod.
    Tato metoda má přístup k proměnné třídy a ke statické metodě, nemá přístup k datům instance.
    class Hample:
        ham = 20                          datový atribut třídy
    
        @classmethod                      dekorace
        def myFunc(cls):                  metoda třídy
            print("Hodnota proměnné třídy: ", cls.ham)
            cls.otherFunc()               volání staticé metody
    
        @staticmethod                     dekorace
        def otherFunc():                  statická metoda
            print("Výstup statické metody.")
    
    >>> Hample.myFunc()                   volání metody třídy
    Hodnota proměnné třídy:  20           výstup metody třídy
    Výstup statické metody.               výstup statické metody
    
  3. Statická metoda je obyčejná funkce, deklarovaná uvnitř třídy. Deklarace je uvedena vestavěným dekorátorem @staticmethod (viz odst. 12).
    Tato metoda má přístup pouze ke svým vlastním atributům. Obě metody lze volat i přes vytvořenou instanci:
  4. >>> hamp = Hample()
    >>> hamp.myFunc()
    Hodnota proměnné třídy:  20
    Výstup statické metody.
    >>> hamp.otherFunc()
    Výstup statické metody.
    

12.4 Inicializační metoda __init__

Protože naše třída Point má představovat matematické body v dvojrozměrném prostoru, všechny její instance by automaticky měly mít atributy x a y. Tak tomu však u našich objektů třídy Point zatím není:

>>> p2 = Point()            # vytvořili jsme instanci

>>> p2.x = 8                # vytvořili jsme atribut přiřazením hodnoty
>>> p2.y                    # bez přiřazení atribut 'y' neexistuje!
Traceback (most recent call last):
    File "<stdin>", line 1, in?
AttributeError: Point instance has no attribute 'y' 
>>>    

Abychom při každé deklaraci objektu nemuseli vytvářet jeho datové atributy ručně, použijeme takzvanou inicializační metodu __init__, kterou přidáme na začátek třídy a pomocí níž potřebné atributy objektu zavedeme předem. Této funci (metodě) se také říká konstruktor.

 class Point:
     def __init__(self, x=0, y=0): 
         self.x = x
         self.y = y    

Prvním parametrem definice __init__ je povinné slovo self, které zastupuje dosud neexistující (ale předpokládanou) instanci třídy. Další parametry s počáteční hodnotou zajišťují, že každá nově vytvořená instance bude implicitně obsahovat datové atributy x, y, zde s počáteční nulovou hodnotou.

Užitečnost konstruktoru si ukážeme ve spojení s další metodou, která určí vzdálenost bodu od počátku souřadnic:

 class Point:
     def __init__(self, x=0, y=0 ):           # konstruktor 
         self.x = x
         self.y = y
     def distance_from_origin(self):          # metoda instance
         return(self.x**2 + self.y**2)**0.5		 

Při vytváření instance musíme zadat hodnotu atributů, deklarovaných v metodě __init__. Pokud jsou tyto atributy deklarovány s počátečními hodnotami, které nám vyhovují - potom nemusíme:

>>> p = Point()
>>> p.x, p.y
(0, 0)
Můžeme zajisté vytvořit objekt s vlastními hodnotami x,y:
>>> g = Point(3,4)
>>> g.x, g.y
(3, 4)
>>> g.distance_from_origin()
5.0

Instance jako parametr

Instanci můžeme zadat jako parametr obvyklým způsobem:

def print_point(obj):
    print('%s, %s' % (obj.x, obj.y))
>>> print_point(g)
3, 4
>>> print_point(p)
0, 0

12.5 Obdélník

Řekněme, že chceme, aby třída představovala obdélník. Otázkou je, jakou informaci musíme poskytnout, abychom určili obdélník? Pro zjednodušení předpokládejme, že je obdélník orientován buďto vodorovně nebo svisle, nikdy není pootočen.

Máme několik možností: můžeme určit střed obdélníka (dvě souřadnice) a jeho velikost (šířku a výšku), nebo můžeme určit jeden z jeho rohů a velikost, nebo můžeme určit polohu dvou protilehlých bodů. Zvolíme si levý horní roh obdélníka a jeho velikost.

class Rectangle:
    def __init__(self, posice, w, h): 
        self.corner = posice
	self.width = w
	self.height = h
Parametr 'posice' předpokládá zadání dvojice čísel - entici. Pro její vložení můžeme použít nepojmenovaný objekt již deklarované třídy Point (první definice v odst. 12.4):
>>> bod = Rectangle(Point(), 100, 200)
>>> bod.width, bod.height
(100, 200)
>>> bod.corner.x, bod.corner.y
(0, 0) 

Objektu Point() jsme nezadali žádné argumenty, protože nám zřejmě vyhovují jeho implicitní hodnoty. Kdybychom chtěli mít počátek obdélníka jinde než v bodě (0,0), mohli bychom například zadat:

>>> box = Rectangle(Point(10,50), 100, 200)
>>> box.corner.x, box.corner.y
(10,50)
>>> box.width, box.height
(100,200)

Vzájemné vztahy objektů vidíme na následujícím obrázku: atribut 'corner' objektu 'box' odkazuje na objekt třídy Point s atributy 'x' a 'y'.


12.6 Instance jsou měnitelné

Změnu instance můžeme provést změnou některého z jejich atributů a to přiřazením. Abychom například změnili velikost obdélníka bez změny jeho polohy, změníme hodnoty width a height:

box.width = box.width + 50
box.height = box.height + 100

Změnu atributů můžeme zobecnit do funkce:

def grow_rectangle(obj, dwidth, dheight):
    obj.width += dwidth
    obj.height += dheight

Můžeme také napsat funkci pro změnu polohy obdélníka:

def move_rectangle(obj, dx, dy):
    obj.corner.x += dx
    obj.corner.y += dy


12.7 Instance jako výstupní hodnoty

Instance může být výstupní hodnotou funkce. Například, funkce find_center přijímá instanci třídy Rectangle jako argument a vrací instanci třídy Point, která obsahuje souřadnice středu zadaného obdélníka:

def find_center(obj):
    p = Point() 
    p.x = obj.corner.x + obj.width/2.0
    p.y = obj.corner.y + obj.height/2.0
    return p

Když si definice tříd Point (odst. 12.4), Rectangle (odst 12.6) a funkcí find_center, print_point vložíme do jednoho skriptu, můžeme v konzole Pythonu psát:

>>> box = Rectangle(Point(40,60), 100, 200)
>>> center = find_center(box)
>>> print_point(center)
90.0, 160.0

Je také možné deklarovat instanci s použitím entice,

>>> boot = Rectangle((40,60), 100, 200)

instance obsahuje atribut 'corner' ale hodnoty tohoto atributu nemají jména:

>>> boot.corner
(40,60)
>>> boot.corner.x
AttributeError: 'tuple' object has no attribute 'x'

Pro takto deklarovaný objekt nám samozřejmě nebudou chodit funkce distance_from_origin(), print_point(obj) (ods. 12.4) a find_center(obj) (odst. 12.7), protože všechny používají jména x,y. Bude nám chodit funkce grow_rectangle(obj,dwidth,dheight) (odst. 12.6).


12.8 Instance uvnitř f-stringu

Instanci lze reflektovat i uvnitř f-stringu (viz kap. 6.17 Formátování řetězců III). Máme-li například definovanou třídu v souboru katalog.py:

class Komik:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

    def __str__(self):
        return f"{self.first_name} {self.last_name}, věk {self.age}."

    def __repr__(self):
        return f"{self.first_name} {self.last_name}, věk {self.age}. Překvapení!"

můžeme ji importovat do interaktivní konzoly, vytvořit instanci a tu posléze aktivovat v f-stringu:

>>> import katalog
>>> komik = katalog.Komik("Jan", "Plíšek", "74")
>>> f"{komik}"               případně  f"{komik!s}"
'Jan Plíšek, věk 74.'
>>> f"{komik!r}"             viz 6.16 Formátování řetězců II
'Jan Plíšek, věk 74.  Překvapení!'      viz soubor katalog.py

Poznámku Překvapení! jsme při uplatnění konverze !r ve výměnném poli získali proto, že jsme si ji nadefinovali v metodě __repr__(self).


12.9 Kopírování třídy

Při kopírování třídy či objektu třídy (instance) použijeme vždy metodu copy nebo deepcopy z modulu copy.
Pro objekty tříd, které neobsahují vnořené objekty, postačí tak zvaná mělká kopie (shallow copy), která vytvoří nezávislou kopii nevnořených objektů. Příkladem takového objektu je třída Point z odst. 12.4:

>>> import copy
 
>>> p1 = Point(3,4)
>>> p2 = copy.copy(p1) 
>>> p1 is p2
False                          # mají různá ID
>>> p1.x == p2.x and p1.y == p2.y
True                           # mají stejné souřadnice

Příkladem vnořeného objektu je atribut corner třídy Rectangle , jejíž dva nevnořené objekty jsou atributy x, y - viz odst. 12.6.

Vytvoříme-li obdélník b1 a jeho kopii b2 funkcí copy,

>>> b1 = Rectangle(Point(), 100, 200)
>>> b2 = copy.copy(b1)
vytvoří se nezávislé kopie pouze atributů with a height, zatímco atribut corner je společný pro obě prezentace b1 a b2:

Nezávislou hlubokou kopii (deep copy) i vnořených objektů vytvoříme metodou deepcopy:

>>> b2 = copy.deepcopy(b1)

12.10 Dekorátory II

Dekorace funkce funkcí je popsána v kapitole 3.11.

Dekorace funkce třídou

Při dekorování funkce třídou zastupuje klauzuru 'wrapper' speciální metoda __call__:

class Pohoda:
    def __init__(self, fun): 
        self.fun = fun 
      
    def __call__(self, *args, **kwargs): 
        # Sem může přijít vhodný kód  
        self.fun(*args, **kwargs)            # volání 'ovečky' 
        # Sem rovněž může přijít vhodný kód 
              
@Pohoda                                      # dekorace třídou
def pozdrav(name, message ='Nazdar'):        # ovečka 
    print("{} {}".format(message, name)) 
>>> pozdrav("Pavle!")
Nazdar Pavle!

Uvedená ukázka je poněkud schematická, protože výstup "Nazdar Pavle!" bychom zajistili i bez dekorátoru. Napravíme to další ukázkou, ve které nám dekorátor doplní výstup z dekorované třídy:

class Decorator:
   def __init__(self, x):
        self.x = x
        
   def __call__(self, a):
        print('Druhá mocnina', a, 'je', self.x(a))
                             # výraz self.x(a) je invokace ovečky
@Decorator
def square(a):
   return a*a
>>> square(5)
Druhá mocnina 5 je 25

Dekorace třídy funkcí

import functools
import time

def timer(func):
    "Vytiskne délku výpočtu dekorované funkce či třídy"
    @functools.wraps(func)                 
    def wrapper(*args, **kwargs):
        start = time.perf_counter()  
        val = func(*args, **kwargs)
        run_time = time.perf_counter() - start  
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return val
    return wrapper

@timer
class Calculator:
    def __init__(self, num):
        self.num = num
        import time
        time.sleep(2)

    def double_and_add(self):
        "Vrátí sumaci stejně modifikovaných členů řady"
        res = sum([i * 2 for i in range(self.num)])
        print("Result : {}".format(res))
>>> c = Calculator(100)
Finished 'Calculator' in 2.0111 secs
>>> c.double_and_add()
Result : 9900

Dekorátor @timer můžeme použít i k dekoraci funkce.


12.11 Speciální metody a atributy

Pro zvídavé

Speciální (také magické) jsou všechny předdefinované metody a atributy tříd, jejichž název je ohraničen dvěma podtržítky, jako například výše popsaná metoda __init__. Jejich alternativní označení v angličtině je dunders (double underscores).

Výpis předdefinovaných metod a atributů pro určitý objekt získáme příkazem dir(), například pro slovník (list):

>>> dir([1,2,3])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__len__',
 '__lt__',  '__mul__', '__ne__', '__new__', '__reduce__',  '__reduce_ex__',
 '__repr__', '__reversed__',  '__rmul__', '__setattr__', '__setitem__',
 '__sizeof__', '__str__', '__subclasshook__',
'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert',
 'pop', 'remove', 'reverse', 'sort']

Speciální metody jsou interně evokované při explicitním volání příslušných operátorů a vestavěných funkcí. Například porovnání "ariel" == "alias" je realizováno speciální metodou __eq__:

>>> "ariel".__eq__("alias")
False

Speciální metodě můžeme zadat specifické chování. Například sečíst souřadnice dvou bodů (provést vektorový součet):

class Point:
    def __init__(self, x=0, y=0):        # speciální metoda (instanční) 
        self.x = x
        self.y = y

    def __add__(self, other):            # speciální metoda (instanční)
        return self.x + other.x, self.y + other.y		
>>> p1 = Point(1, 2)
>>> p2 = Point(3, 4)
>>> p1 + p2                       # interně  p1.__add__(p2)
(4, 6)

Specielními atributy jsou datové objekty __doc__, __name__, __dict__, ... . Atribut __doc__obsahuje informaci o příslušném objektu; například o prázdném seznamu se dozvíme:

>>> [].__doc__
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified. 

Dobrý přehled specielních metod lze nalézt na stránce tutoriálu Ponořme se do Pythonu 3


12.12 Datové třídy

Datová třída (dataclass) je speciální třída, vhodná pro ukládání datových objektů, která je vytvořená s pomocí dekorátoru @dataclass, importovaného z modulu dataclasses.

Tento dekorátor automaticky generuje všechny potřebné dunder metody (__init__(), __repr__(), ...), podporující zjednodušené zadávání datových atributů, zde zvaných pole (fields). Deklarace pole má skladbu:   name:   type   [ = value ].

dunder: společné označení metod, jejichž názvy jsou vymezeny dvojitými podtržítky (double-underscore).

from dataclasses import dataclass

@dataclass
class Contact:
    name: str
    email: str
    phone: str = "00 00 00 00" 
>>> contact = Contact("test","test@test.com", '72 63 08 56')
>>> contact
Contact(name='test', email='test@test.com', phone='72 63 08 56')

Lze použít také klíčové argumenty:

>>> contact1 = Contact(name="test1",email="test1@test.com")
>>> contact1
Contact(name='test1', email='test1@test.com', phone='00 00 00 00')

12.13 Dědění

Mechanizmus, zvaný dědění (inheritance) se používá při odvození nové třídy z třídy stávající. Odvozená třída (sub class, sub typ, potomek) přebírá atributy a metody (bázové) třídy výchozí (super class, rodič či předek).

Skladba pro deklaraci subtřídy je jednoduchá:

class NázevOdvozenéTřídy(NázevVýchozíTřídy):
    '''Nepovinný dokumentační řetězec - docstring '''
    pass

Kromě naznačeného dědění po jediném předkovi (simple heritance) existují další možnosti:

Instruktivní ukázka s použitím speciální metody __init__:

class Savci:
    vid = "Pozdrav Pánbůh"
    def __init__(self, druh):
        hid = "pane Randák"
        print(druh, 'je teplokrevný savec.')
		        
class Pes(Savci):
    def __init__(self):
        super().__init__('Pes')            Varianta: Savci.__init__(self, 'Pes')
	    print('Pes je přítel člověka.')

Speciální metoda __init__ třídy Pes potlačí tutéž metodu třídy Savci. Toto chování však zruší následně definovaná metoda __init__ s objektem super() (nebo Savci), čímž umožňuje použití atributů děděné třídy.

>>> pajda = Pes()
Pes je teplokrevný savec.
Pes je přítel člověka.

Zadání: Vypusťte v definici třídy Pes řádek "super().__init__('Pes')" a a zkuste pro objekt pajda volat proměnné vid a hid.

Obsahuje-li zdrojová i děděná třída stejný atribut, platí pro děděnou třídu vlastní definice tohoto atributu:

class A:
    pes = "Dingo" 
    def zobraz(self):
        print ('Jsem třída A.')

class B(A):
    pes = "Voříšek" 
    def zobraz(self):
        print ('Jsem třída B.')
>>> obj = B()
>>> obj.pes
'Voříšek'
>>> obj.zobraz()
Jsem třída B.

12.14 Propojení tříd

Propojení tříd lze kromě dědění provést také takzvanou kompozicí. Propojení se uskuteční prostřednictvím instance třídy uvnitř jiné třídy:

class Raketa:
    def __init__(self, name, destinace):
        self.name = name
        self.distance = destinace

    def launch(self):
        return "%s dosedl na %s" % (self.name, self.destinace)
                             
class Lunochod():
    def __init__(self, name, destinace, maker):
        self.raketa = Raketa(name, destinace)   # deklarace instance
        self.maker = maker

    def report(self):
        return "%s byl spuštěn korporací %s" % (self.raketa.name, self.maker)
>>> z = Lunochod("Lunochod", "Měsíc", "EKA")
>>> z.raketa.launch()
'Lunochod dosedl na Měsíc'
>>> z.report()
'Lunochod byl spuštěn korporací EKA'

12.15 Glosář

třída (class)
Uživatelsky definovaný typ. Třídu lze považovat za prototyp objektů, určující jejich vlastnosti a chování.
instance
Konkrétní objekt určité třídy.
objekt (object)
Všechno v Pythonu je objekt. Zejména 'instance' se často označuje jako objekt.
konstruktor (constructor)
Metoda __init__, kterou Python automaticky použije při tvorbě nové instance.
atribut (attribute)
Společné označení pro proměnné třídy i objektů a metody.
mělká rovnost (shallow equality)
Rovnost odkazů, nebo dvou odkazů, které ukazují na stejný objekt.
hluboká rovnost (deep equality)
Rovnost hodnot, nebo dvou odkazů, které ukazují na objekty se stejnými hodnotami.
mělká kopie (shallow copy)
Kopie objektu včetně odkazů na vnořené objekty provedená příkazem copy.copy(item); vnořené objekty se nezkopírují, pouze odkazy.
hluboká kopie (deep copy)
Kopie objektu včetně jeho vnořených objektů provedená příkazem copy.deepcopy(item)

12.16 Cvičení

  1. Do třídy Rectangle vložte metodu area, která vrací plochu obdélníka
    r = Rectangle(Point(0,0), 50,100)
    # test: r.area() --> 5000
    
  2. Do třídy Rectangle vložte metodu perimeter, která vrací obvod obdélníka
    r = Rectangle(Point(0,0), 50,100)
    # test: r.perimeter() --> 300
    
  3. Do třídy Rectangle vložte metodu flip, která zamění jeho šířku za výšku a obráceně
  4. Ke třídě Point (viz 12.4) přidejte metodu sklon, která vrátí úhel sklonu spojnice bodu x,y s počátkem 0,0 v radiánech. Její evokace může vypadat takto:

    >>> Point(4,10).sklon()
    Sklon v radiánech:  1.1902899496825317
    

    Ošetřte případ, kdy je spojnice kolmá k ose x (ψ = π/2).

  5. Upravte skript třídy Punktum:

    class Punktum:
        def __init__(self, x, y): 
            self.x = x
            self.y = y
    	def __repr__(self):
            return f"Souřadnice bodu: x = {self.x}," \
    		                        f"y = {self.y}"	
    

    tak aby její volání mělo následující výstup

    >>> Punktum(5,9)
    Souřadnice bodu: x = 5, y = 9
    

previous up next hi end end