diff --git a/09-objekty/09-objekty.tex b/09-objekty/09-objekty.tex
new file mode 100644
index 0000000000000000000000000000000000000000..3064f49c3dec3f15fad18f5268f1f0d72f972952
--- /dev/null
+++ b/09-objekty/09-objekty.tex
@@ -0,0 +1,297 @@
+\documentclass{beamer}
+\usepackage[utf8]{inputenc}
+\usepackage[czech]{babel}
+\usepackage{palatino}
+\usepackage{verbatim}
+\usetheme{Warsaw}
+\title{Programování 1: Třídy a objekty}
+\author[Martin Mareš]{Martin Mareš\\\texttt{mj@ucw.cz}}
+\institute{Katedra Aplikované Matematiky\\MFF UK Praha}
+\date{2019}
+\begin{document}
+\setbeamertemplate{navigation symbols}{}
+\setbeamertemplate{footline}{}
+\setbeamerfont{title page}{family=\rmfamily}
+\shorthandoff{"}
+
+\begin{frame}
+\titlepage
+\end{frame}
+
+\input ../slidemac.tex
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Definice třídy}
+
+\verbatiminput{trida_def.py}
+
+~
+
+Definujeme nový typ, který má nějaké {\bf atributy} (vlastnosti)
+a~{\bf metody} (funkce, operace).
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Objekty}
+
+Vytvoříme nový objekt (automaticky zavolá {\tt \_\_init\_\_}):
+\smallskip
+
+\py{%
+azor = Zvire("Azor", "Haf!")\\
+azor
+}{%
+<Zvire object at 0x7ffff71ce2b0>
+}
+
+\smallskip
+Atributy objektu:
+\smallskip
+
+\py{%
+azor.zvuk
+}{%
+'Haf!'
+}
+
+\py{%
+azor.zvuk = "Hafff!"
+}{%
+}
+
+\smallskip
+Metody objektu:
+\smallskip
+
+\py{%
+azor.slysi\_na('Příšero')
+}{%
+False
+}
+
+\py{%
+azor.ozvi\_se()
+}{%
+Azor říká: Hafff!
+}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Identita objektu}
+
+\py{%
+jezevcik = Zvire("Špagetka", "haf")\\
+bernardyn = Zvire("Bernard", "HAF!!!")\\
+maxipes = bernardyn\\
+maxipes.jmeno = "Fík"\\
+bernardyn.jmeno
+}{%
+'Fík'
+}
+
+\py{%
+type(jezevcik)
+}{%
+<class 'Zvire'>
+}
+
+\py{%
+id(jezevcik), id(bernardyn), id(maxipes)
+}{%
+(737339253592, 737339253704, 737339253704)
+}
+
+\py{%
+bernardyn is maxipes
+}{%
+True
+}
+
+\py{%
+bernardyn is jezevcik
+}{%
+False
+}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Protokol pro převod na řetězec}
+
+\verbatiminput{trida_str.py}
+
+\medskip
+
+\py{%
+hroch = Zvire("Hippo", "Humpf!")\\
+hroch
+}{%
+Pes(Hippo, Humpf!)
+}
+
+\py{%
+print(hroch)
+}{%
+Hippo
+}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Protokol pro operátory}
+
+\verbatiminput{trida_op.py}
+
+\medskip
+
+\py{%
+hroch1 = Zvire("Hippo", "Humpf!")\\
+hroch2 = Zvire("Hippo", "Humpf!")\\
+hroch1 is hroch2
+}{%
+False
+}
+
+\py{%
+hroch1 == hroch2
+}{%
+True
+}
+
+\smallskip
+
+Podobně jde předefinovat všechny operátory včetně {\bf []} a {\bf .}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Dokumentační komentáře}
+
+\verbatiminput{trida_doc.py}
+
+\py{%
+help(Zvire)
+}{%
+}
+
+\py{%
+z = Zvire("Lenochod", "Zzz...")\\
+help(z.slysi\_na)
+}{%
+}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Dědičnost}
+
+\verbatiminput{trida_dedicnost.py}
+
+\py{%
+k = Kocka("Příšerka", "Mňauuu")\\
+k.slysi\_na("Příserka")  \cmt{(speciální kočičí verze)}
+}{%
+False
+}
+
+\py{%
+k.ozvi\_se()  \cmt{(původní zvířecí metoda)}
+}{%
+Příšerka říká: Mňauuu
+}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Jak to funguje uvnitř}
+
+{\bf Prostory jmen (namespaces):}
+
+\medskip
+
+\begin{itemize}
+\item Zabudované funkce (třeba {\tt print})
+\item Globální jména (proměnné, funkce)
+\item Lokální jména uvnitř funkce
+\item Jména definovaná v~třídě
+\item Jména definovaná v~objektu
+\end{itemize}
+
+\medskip
+
+Obyčejné jméno se hledá ve všech prostorech, které jsou na daném
+místě v~programu \uv{vidět}.
+
+\medskip
+
+{\tt objekt.jméno} se hledá:
+
+\medskip
+
+\begin{itemize}
+\item V~prostoru atributů objektu
+\item V~prostoru příslušné třídy
+\item V~prostorech všech nadřazených tříd
+\item Pozor, třída může mít více přímých předků!
+\end{itemize}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\begin{frame}{Jak to funguje uvnitř: zabudované typy}
+
+{\bf Zabudované typy jako int, str apod.} jsou rovněž třídy.
+Volání {\tt int()} nebo {\tt int("1")} je prostě vytvoření objektu dané třídy.
+
+\medskip
+
+{\bf Třída je také objekt:}
+
+\medskip
+
+\begin{itemize}
+\item Lze psát třeba {\tt Zvire.slysi\_na}.
+\item Uvnitř třídy můžeme přiřazovat do proměnných, ty jsou vidět jako
+      atributy třídy.
+\item Vnitřek definice {\tt class} je ve skutečnosti normální program,
+      který se provádí uvnitř prostoru jmen třídy.
+\end{itemize}
+
+\medskip
+
+{\bf Modul je také objekt:}
+
+\medskip
+
+\begin{itemize}
+\item {\tt import math} ho vytvoří
+\item {\tt math.random} je formálně přístup k~atributu
+\end{itemize}
+
+\medskip
+
+{\bf Volání metody:}
+
+\medskip
+
+\begin{itemize}
+\item {\tt alik.ozvi\_se} vytvoří pomocnou funkci, která zavolá
+      {\tt Zvire.ozvi\_se} a doplní jako první argument {\tt alik}.
+\end{itemize}
+
+\end{frame}
+
+% ----------------------------------------------------------------------
+
+\end{document}
diff --git a/09-objekty/Makefile b/09-objekty/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e48df267f35fff03c55f4dbbc81e15c050a159c7
--- /dev/null
+++ b/09-objekty/Makefile
@@ -0,0 +1,3 @@
+SLIDES=09-objekty.pdf
+
+include ../Makerules
diff --git a/09-objekty/trida.py b/09-objekty/trida.py
new file mode 100644
index 0000000000000000000000000000000000000000..c98e92b0fda39e273ec1f76999898e71706101f3
--- /dev/null
+++ b/09-objekty/trida.py
@@ -0,0 +1,35 @@
+class Zvire:
+    """Vytvoří zvíře s danými vlastnostmi."""
+
+    def __init__(self, jmeno, zvuk):
+        self.jmeno = jmeno
+        self.zvuk = zvuk
+
+    def slysi_na(self, jmeno):
+        """Slyší zvíře na dané jméno?"""
+        return self.jmeno == jmeno
+
+    def ozvi_se(self):
+        """Vydá zvuk daného zvířete."""
+        print(self.jmeno, "říká:", self.zvuk)
+
+    def __str__(self):
+        return self.jmeno
+
+    def __repr__(self):
+        return f"Pes({self.jmeno}, {self.zvuk})"
+
+    def __eq__(self, other):
+        return self.jmeno == other.jmeno and \
+               self.zvuk == other.zvuk
+
+class Kocka(Zvire):
+    """Vytvoří kočku s danými vlastnostmi."""
+
+    def __init__(self, jmeno, zvuk):
+        Zvire.__init__(self, jmeno, zvuk)
+        self.pocet_zivotu = 9
+
+    def slysi_na(self, jmeno):
+        # Copak kočka slyší na jméno?
+        return False
diff --git a/09-objekty/trida_dedicnost.py b/09-objekty/trida_dedicnost.py
new file mode 100644
index 0000000000000000000000000000000000000000..802fbaaff05c6c39ba71b83088a7024ec78310be
--- /dev/null
+++ b/09-objekty/trida_dedicnost.py
@@ -0,0 +1,9 @@
+class Kocka(Zvire):
+
+    def __init__(self, jmeno, zvuk):
+        Zvire.__init__(self, jmeno, zvuk)
+        self._pocet_zivotu = 9   # interní
+
+    def slysi_na(self, jmeno):
+        # Copak kočka slyší na jméno?
+        return False
diff --git a/09-objekty/trida_def.py b/09-objekty/trida_def.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2f073068d5993a59f7c098f0eb3799416230c56
--- /dev/null
+++ b/09-objekty/trida_def.py
@@ -0,0 +1,11 @@
+class Zvire:
+
+    def __init__(self, jmeno, zvuk):
+        self.jmeno = jmeno
+        self.zvuk = zvuk
+
+    def slysi_na(self, jmeno):
+        return self.jmeno == jmeno
+
+    def ozvi_se(self):
+        print(self.jmeno, "říká:", self.zvuk)
diff --git a/09-objekty/trida_doc.py b/09-objekty/trida_doc.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca433f1e07bd08ca2c80f134048f4a22168385f1
--- /dev/null
+++ b/09-objekty/trida_doc.py
@@ -0,0 +1,10 @@
+class Zvire:
+    """Vytvoří zvíře s danými vlastnostmi."""
+
+    def __init__(self, jmeno, zvuk):
+        self.jmeno = jmeno
+        self.zvuk = zvuk
+
+    def slysi_na(self, jmeno):
+        """Slyší zvíře na dané jméno?"""
+        return self.jmeno == jmeno
diff --git a/09-objekty/trida_op.py b/09-objekty/trida_op.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae93a77b004999b2d6f19f9a71ff5fc4e5a74cc4
--- /dev/null
+++ b/09-objekty/trida_op.py
@@ -0,0 +1,8 @@
+class Zvire:
+    def __init__(self, jmeno, zvuk):
+        self.jmeno = jmeno
+        self.zvuk = zvuk
+
+    def __eq__(self, other):
+        return self.jmeno == other.jmeno and \
+               self.zvuk == other.zvuk
diff --git a/09-objekty/trida_str.py b/09-objekty/trida_str.py
new file mode 100644
index 0000000000000000000000000000000000000000..d7742c9deea5bb567beb2d24048a0ad2d04bda66
--- /dev/null
+++ b/09-objekty/trida_str.py
@@ -0,0 +1,10 @@
+class Zvire:
+    def __init__(self, jmeno, zvuk):
+        self.jmeno = jmeno
+        self.zvuk = zvuk
+
+    def __str__(self):
+        return self.jmeno
+
+    def __repr__(self):
+        return f"Pes({self.jmeno}, {self.zvuk})"
diff --git a/TODO b/TODO
index 6d4b7e13beda62393dc32c6b8d51d74f9bde6674..f6697236b1846bd91dd95bd9cc4546292a8180ea 100644
--- a/TODO
+++ b/TODO
@@ -5,6 +5,6 @@
 - sort(key=...)
 - funkce vyšších řádů
 - rekurze
-- comprehension pro slovníky a množiny
+- životnost objektů, destruktory, weak references
 
 - příště u domácích úkolů napsat, jak velké vstupy mají zvládat