{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 8. előadás\n",
    "_Tartalom:_ osztályok, objektum orientált programozás\n",
    "\n",
    "\n",
    "A Python objektum orientált programozást a 2D-s pontok osztályának elkészítésén keresztül mutatjuk be _(Nagyban támaszkodni fogunk Siki Zoltán könyvére)_. A Python nyelvben minden osztály (például a lista vagy a szótár is, de a függvények is). \n",
    "Kezdjük is el az osztály kódjának elkészítését.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Siki Zoltán\n",
    "class Point2D(object): # object a bázis osztály\n",
    "    \"\"\" kettő dimenziós pontok osztálya\n",
    "    \"\"\"\n",
    "    def __init__(self, east = 0, north = 0): # __init__ a konstruktor\n",
    "        \"\"\" Initialize point\n",
    "        :param east: első koordináta\n",
    "        :param north: második koordináta\n",
    "        \"\"\"\n",
    "        #self.east = east # tagváltozó létrehozása\n",
    "        self.setEast(east)\n",
    "        #self.north = north\n",
    "        self.setNorth(north)\n",
    "    def abs(self):\n",
    "        \"\"\" calculate length of vector (absolute value)\n",
    "        :returns: absolute value\n",
    "        \"\"\"\n",
    "        return (self._east**2 + self._north**2)**0.5\n",
    "    def __str__(self):\n",
    "        \"\"\" String representation of the 2D point\n",
    "        :returns: point coordinates as string (e.g. 5; 3)\n",
    "        \"\"\"\n",
    "        return \"*%.3f; %.3f*\" % (self._east, self._north)\n",
    "    def __add__(self, p):\n",
    "        \"\"\" Add two point\n",
    "        :param p: point to add\n",
    "        :returns: the sum of the two point (vector)\n",
    "        \"\"\"\n",
    "        return Point2D(self._east + p.getEast(), self._north + p.getNorth())\n",
    "    def setEast(self, east):\n",
    "        self._east = east\n",
    "    def getEast(self):\n",
    "        return self._east\n",
    "    def setNorth(self, north):\n",
    "        if north < 400000:\n",
    "            self._north = north\n",
    "        else:\n",
    "            raise ValueError('north must be less than 400000') \n",
    "    def getNorth(self):\n",
    "        return self._north "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A `self` változó az osztály minden (nem statikus vagy osztály) metódusának az első paramétere és az objektum példányt jelenti, ezen keresztül hivatkozhatunk az objektum elemeire pl. `self.east`. A többsoros megjegyzés a sphinx programnak megfelelően készült, hogy automatikusan lehessen dokumentációt generálni a kódból.\n",
    "Próbáljuk ki a kódunkat."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "p0 = Point2D()          # példányosítás, 2, 0 pont\n",
    "p1 = Point2D(0)         # példányosítás, 0, 0 pont\n",
    "p2 = Point2D(2, -2)     # példányosítás, 2,–2 pont"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Írassuk ki az egyes pontok tagváltozóit, abszolút értékét."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0, 0] abs: 0.00\n",
      "[ 0, 0] abs: 0.00\n",
      "[ 2,-2] abs: 2.83\n"
     ]
    }
   ],
   "source": [
    "pontok = [p0, p1, p2]\n",
    "for  pont in pontok:\n",
    "    print(\"[%2d,%2d] abs: %.2f\"  % (pont.getEast(), pont.getNorth(), pont.abs()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p2 east tagváltozó értéke:  2\n",
      "p2 north tagváltozó értéke:  -2\n",
      "p2 abs() függvény értéke:  2.8284271247461903\n",
      "p2.__dict__: {'_east': 2, '_north': -2}\n",
      "p2: *2.000; -2.000*\n"
     ]
    }
   ],
   "source": [
    "print(\"p2 east tagváltozó értéke: \", p2.getEast())\n",
    "print(\"p2 north tagváltozó értéke: \", p2.getNorth())\n",
    "print(\"p2 abs() függvény értéke: \", p2.abs())\n",
    "print(\"p2.__dict__:\", p2.__dict__)\n",
    "print(\"p2:\", p2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Az egyes osztályokhoz speciális függvényeket definiálhatunk (mint pl. az `__init__`), ezek jellemzője, hogy két aláhúzás karakterrel kezdődik és végződik a nevük. \n",
    "\n",
    "Az `__init__` függvény az úgynevezett **konstruktor**, ez fut le a példányok létrehozásakor, itt biztosíthatjuk, hogy ne legyen inicializálatlan tagváltozónk. \n",
    "\n",
    "A **destruktor**t a `__del__` függvény implementálásával valósíthatjuk meg. A dinamikus tárfoglalás hiányában a Pythonban erre\n",
    "ritkábban van szükség.\n",
    "\n",
    "A print utasítás az osztály `__str__` metódusát hívja meg. A fenti példában ennek alapértelmezett változatának eredményét láthattuk az object osztályból. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Point2D at 0x28b29bc06d8>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*2.000; -2.000*\n"
     ]
    }
   ],
   "source": [
    "print(p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*10.000; 300000.000*\n"
     ]
    }
   ],
   "source": [
    "p3 = Point2D(10, 300000) \n",
    "print(p3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A pont osztály egy példányának a koordinátáit közvetlenül nem változtathatjuk meg amennyiben:\n",
    "``` python\n",
    "self.east = east\n",
    "```\n",
    "helyett a \n",
    "``` python\n",
    "self.setEast(east)\n",
    "```\n",
    "módon hozzuk létre tagváltozóinkat. \n",
    "Ha a pont osztály egy példányának a koordinátáit közvetlenül megváltoztathatjuk, ez azzal a következménnyel járhat, hogy:\n",
    "1. a példány tagváltozóit az osztály felhasználója egy programhibából következően is átírhatja,\n",
    "2. a példány tagváltozóit ellenőrzés nélkül is felül lehet írni.\n",
    "\n",
    "Ennek kivédésére privát tagváltozókat (`_` karakterrel kezdődő név) és getter/setter metódusokat használhatunk. Írjuk át az osztályunkat, hogy egy-egy metóduson keresztül lehessen megváltoztatni a tagváltozókat. A Python osztályok privát tagváltozóinak neve aláhúzás karakterrel kezdődik. A két aláhúzással kezdődő és végződő nevek a Python nyelv elemei.\n",
    "\n",
    "A következő kódrészlet\n",
    "``` python\n",
    "    def setNorth(self, north):\n",
    "        if north < 400000:\n",
    "            self._north = north\n",
    "        else:\n",
    "            raise ValueError('north must be less than 400000')\n",
    "```\n",
    "miatt a \n",
    "\n",
    "``` python\n",
    "p3.setNorth(400000) \n",
    "```\n",
    "illetve \n",
    "``` python\n",
    "p9 = Point2D(10, 400001) \n",
    "```\n",
    "\n",
    "esetén a következő hibába botlanánk:\n",
    "``` python\n",
    "ValueError Traceback (most recent call last)\n",
    "ValueError: north must be less than 400000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A Python nyelvben az osztályokhoz további speciális függvényekkel műveleteket is definiálhatunk. A pontokat mint helyvektorokat is használhatjuk, készítsük el a helyvektorok összeadását.\n",
    "\n",
    "``` python\n",
    "    def __add__(self, p):\n",
    "        \"\"\" Add two point\n",
    "        :param p: point to add\n",
    "        :returns: the sum of the two point (vector)\n",
    "        \"\"\"\n",
    "        return Point2D(self.east + p.east, self._north + p._north)\n",
    "```\n",
    "\n",
    "Próbáljuk ki:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*2.000; -2.000*\n",
      "*10.000; 300000.000*\n",
      "Összegük:  *12.000; 299998.000*\n"
     ]
    }
   ],
   "source": [
    "print(p2)\n",
    "print(p3)\n",
    "print(\"Összegük: \", p2 + p3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### _Used sources_ / Felhasznált források\n",
    "- [Shannon Turner: Python lessons repository](https://github.com/shannonturner/python-lessons) MIT license (c) Shannon Turner 2013-2014\n",
    "- [Siki Zoltán: Python mogyoróhéjban](http://www.agt.bme.hu/gis/python/python_oktato.pdf) GNU FDL license (c) Siki Zoltán"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}