{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.upm.es/sfs/Rectorado/Gabinete%20del%20Rector/Logos/UPM/EscPolitecnica/EscUpmPolit_p.gif \"UPM\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Course Notes for Learning Intelligent Systems"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Department of Telematic Engineering Systems, Universidad Politécnica de Madrid, © Carlos A. Iglesias"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Classes\n",
    "\n",
    "In Python everthing is an object. Classes allow programmers to define their own object types.\n",
    "\n",
    "Classes are defined with the **class** reserved keyword."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Instance attributes and methods\n",
    "\n",
    "The first argument of instance class method is self, that refers to the current instance of the class.\n",
    "There is a special method, __init__ that initializes the object. It is like a constructor, but the object is already created when __init__ is called.\n",
    "\n",
    "Instance attributes are define as self.variables. (self is the same than this in Java)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Example class declaration\n",
    "class TV_Set:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.status = 'off'\n",
    "\n",
    "    def on(self):\n",
    "        self.status = 'on'\n",
    "\n",
    "    def off(self):\n",
    "        self.status = 'off'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Example object instantiation\n",
    "\n",
    "my_tv = TV_Set('Samsung')\n",
    "print(my_tv, my_tv.status)\n",
    "type(my_tv)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call on method\n",
    "my_tv.on()\n",
    "print(my_tv.name, my_tv.status)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Class attributes\n",
    "We can also define class variables and class methods. \n",
    "\n",
    "Class variables are shared across all class instances. Class methods are called directly in the class. (In Java, this was defined with the keyword static)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Example class declaration\n",
    "class TV_Set:\n",
    "    num_tvs = 0\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.status = 'off'\n",
    "        TV_Set.num_tvs += 1\n",
    "\n",
    "    def on(self):\n",
    "        self.status = 'on'\n",
    "\n",
    "    def off(self):\n",
    "        self.status = 'off'\n",
    "\n",
    "print(TV_Set.num_tvs)\n",
    "\n",
    "tv_1 = TV_Set('LG')\n",
    "print(TV_Set.num_tvs)\n",
    "\n",
    "tv_2 = TV_Set('Samsung')\n",
    "print(TV_Set.num_tvs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Special or Magic methods\n",
    "\n",
    "[Special methods](https://docs.python.org/2/reference/datamodel.html#specialnames) allow programmers to customize a class. The convention for special methods is \\__method\\__.\n",
    "\n",
    "We have already seen a special method: \\__init\\__. Other special methods are \\__str\\__ (self) for printing) or \\__eq\\__(self, other) for comparing if two objects are equal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        \n",
    "p = Person('Pedro', 10)\n",
    "p.age = 0\n",
    "print(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example __str(self)__\n",
    "\n",
    "class Person:\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    \n",
    "    def __str__(self):\n",
    "        return 'name: ' + self.name + ', ' + 'age: ' + str(self.age) # str() for converting int into string\n",
    "        \n",
    "p = Person('Pedro', 10)\n",
    "p.age = 0\n",
    "print(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Private attributes and methods\n",
    "\n",
    "In Python, all methods and attributes are public. However, Python uses the following conventions\n",
    "\n",
    "* **Names prefixed by a single leading underscore**:  (e.g. self._a, _\\_calculate()): private use. The statement import does not import objects whose name starts with an underscore. In fact, for class inheritance it has the semantics of *protected* (the subclass would have access to the method / attribute).\n",
    "\n",
    "* **Names prefixed by a double leading underscore** (e.g. \\_\\_calculate(), self.\\_\\_boo): used to avoid a method to be overridden by a subclass. The interpreter invokes name mangling, (inside class FooBar, \\_\\_boo becomes _FooBar\\__boo). This is not used as widely as a single leading underscore."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Instead of Getters and Setters, Python uses Properties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Now we could change the age of Pedro to a negative value\n",
    "p.age = -1\n",
    "print(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In Java, we would define age as private, and getters and setters. In Python, we can use **properties** when we want to control how they are accessed, without changing the interface."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    \n",
    "    def __str__(self):\n",
    "        return 'name: ' + self.name + ', ' + 'age: ' + str(self.age) # str() for converting int into string\n",
    "    \n",
    "    @property                # property for getter, in this case it is not needed\n",
    "    def age(self):\n",
    "        return self._age \n",
    "    \n",
    "    @age.setter\n",
    "    def age(self, val):\n",
    "        if (val < 0):\n",
    "            self._age = 0  # A better alternative would be to throw an exception: raise ValueError(\"Age < 0\")\n",
    "        else:\n",
    "            self._age = val\n",
    "            \n",
    "p = Person('Pedro', -1)\n",
    "print(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Licence"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The notebook is freely licensed under under the [Creative Commons Attribution Share-Alike license](https://creativecommons.org/licenses/by/2.0/).  \n",
    "\n",
    "© Carlos A. Iglesias, Universidad Politécnica de Madrid."
   ]
  }
 ],
 "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.7.1"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
