{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Definition of a class\n",
    "\n",
    "An object is an element that contains data and functions.\n",
    "\n",
    "In python, everything that is put into a variable is an object.\n",
    "\n",
    "Data stored in an object are placed inside variables that we call **attributes**.\n",
    "The functions that are present in the object are called **method functions**.\n",
    "\n",
    "The nature of data and of present functions in an object depend on the type of the object.\n",
    "\n",
    "For example, the integer $5$ is an object. Its type is 'Integer' and it contains a lot of attributes and method functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "type(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a.N()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There exists **private** functions and attributes that do not appear to the user.\n",
    "Their names all begin by an underscore \"\\_\".\n",
    "\n",
    "There are three types of attributes/functions\n",
    " - the attributes/functions that start and end by \"\\_\"\n",
    " - the attributes/functions that start with \"\\_\" only\n",
    " - the attributes/functions that start with \"\\_\\_\" and end by \"\\_\\_\" (2 underscores)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a.__abs__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a._ascii_art_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a._add_parent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All these attributes are attributes that are hidden to the user.\n",
    "This means that the user should not \"generally\" use them, these functions are used by the developers of the class and must be used in very particular cases."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The attributes/functions that start with \"\\_\" and end by \"\\_\" are functions used by the system sage to manipulate the object.\n",
    "\n",
    "For example, '\\_latex\\_' is a function that returns the latex code of the object. If this function exists in an object, Sage knows how to convert it to latex. It will use that function.\n",
    "\n",
    "The attributes that start with \"\\_\" only are private functions created by the developer for the developer.\n",
    "\n",
    "The attributes/functions that start with \"\\_\\_\" and end by \"\\_\\_\" are functions used by python to manipulate the objects.\n",
    "\n",
    "For example, \"\\_\\_str\\_\\_\" is the function that returns the string associated to an object. It is used by python to print the object in the terminal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "M = Matrix([[1,2,3],[4,5,6]])\n",
    "M._latex_()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "latex(M)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "str(M)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "print(M)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is possible to define your own type to create your own objects.\n",
    "\n",
    "For this, we create a class. A class contains all the information that characterize the object.\n",
    "The name of the class is also the type of the object that will be created with this class.\n",
    "\n",
    "A class is like the blueprint of a car:\n",
    " - With a blueprint, you create as many cars as you like\n",
    " - With a class, you can create as many objects as you like\n",
    " \n",
    " Here is the minimal code to create a class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Test:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We just created a class \"Test\" and a new type of object \"Test\"\n",
    "\n",
    "To create an object of type \"Test\", you only need to write:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "e1 = Test()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Test instance at 0x1a059fc20>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We say that \"e1\" is an **instance** of \"Test\".\n",
    "\n",
    "The code \"0x19ed61b48\" (or something similar) is the adresse where the object is located in the memory.This is a unique identifier. Indeed, we can create as many objects as we want with the same type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Test instance at 0x1a05c3440>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e2 = Test(); e2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The variables e1 and e2 contain different objects.\n",
    "They have different addresses.\n",
    "\n",
    "To know if the two object are the same, you can use the operator \"is\":"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "e1 is e2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Watch out! Two objects may be equal, but be represented by two **distinct** objects in the memory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l1 = [1,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l2 = [1,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "l1 is l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "l1 == l2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Certain objects are unique in python, this means that they are represented in a unique way in the memory. This is the case for strings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l1 = 'cou'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l2 = 'cou'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 is l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 == l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The data located in an object can evolve with time.\n",
    "You can for example, add attributes during the life of an object :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "e1.a = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now e1 contains an attribute *a* , while e2 with the same type does not contain an attribute \"a\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "e2.a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise** :\n",
    "\n",
    "Create a new type of object with name \"Car\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create 3 instances c1, c2, c3 of Car"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, add an attribute 'a' for v1 containing 1, an attribute 'b' contenant 1 for v2 and an attribute 'a' containing 4 for v3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Verify that these objects all have different representation in the memory:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Type the following command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "g = v1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Are g and v1 refering to the same object in the memory? Check this."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Predefine attributes and functions\n",
    "\n",
    "We can predefine attributes and functions inside the class of the object.\n",
    "\n",
    "To define a class method, you declare the function inside the class by putting \"self\" as a first input parameter.\n",
    "When a function is called from an object $o$, the parameter *self* will contain the object $o$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Obj:\n",
    "    def f(self, p1, p2):\n",
    "        print(\"Call of %s.f(%s, %s)\"%(str(self),str(p1), str(p2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "o1 = Obj()\n",
    "o2 = Obj()\n",
    "o1.f(1,2)\n",
    "o2.f(3,4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have already seen that there are special functions in python. A special function always starts with two \"\\_\".\n",
    "\n",
    "For example, find the following function on the integer 5: \\_\\_str\\_\\_, \\_\\_repr\\_\\_, \\_\\_int\\_\\_, \\_\\_plus\\_\\_, \\_\\_mult\\_\\_, etc ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try to find in the documentation of these functions.\n",
    "\n",
    "The function **\\_\\_init\\_\\_(self, ...)** is special because this function is executed when an object is created. Usually, the attributes of the object are declared in this function.\n",
    "\n",
    "We called this function the \"constructor\" of the class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Obj:\n",
    "    def __init__(self, a):\n",
    "        self.val = a^2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "e3 = Obj(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "e3.val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "e3 = Obj(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "e3.val"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Inheritance\n",
    "\n",
    "Sometimes, we write many times the same code. That is why we try to reduce the code to save time and to avoid having many times the same mistake.\n",
    "\n",
    "This can be done using inheritance.\n",
    "\n",
    "Suppose that we would like to implement a geometrical shape: a square, a rectangle and a convex polygon.\n",
    "Assume that we wish to implement the function *number_of_corners(self)* that returns the number of vertices located on the perimeter of the figure.\n",
    "\n",
    "We can proceed as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class convex_polygon:\n",
    "    def __init__( self, points ):\n",
    "        self.points = points\n",
    "    def number_of_corners(self):\n",
    "        return len(self.points)\n",
    "    \n",
    "class rectangle:\n",
    "    def __init__( self, points ):\n",
    "        self.points = points\n",
    "    def number_of_corners(self):\n",
    "        return len(self.points)\n",
    "    \n",
    "class square:\n",
    "    def __init__( self, points ):\n",
    "        self.points = points\n",
    "    def number_of_corners(self):\n",
    "        return len(self.points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is clear that we can copy three time the same code.\n",
    "\n",
    "In fact, this is not necessary, because a square is a special rectangle and a rectangle is a special convex polygon.\n",
    "\n",
    "In fact, we can avoid these three copies and explain to python the relation between these three classes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class convex_polygon:\n",
    "    def __init__( self, points ):\n",
    "        self.points = points\n",
    "    def number_of_corners(self):\n",
    "        return len(self.points)\n",
    "    \n",
    "class rectangle(convex_polygon):\n",
    "    pass\n",
    "    \n",
    "class square(rectangle):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We then say that \"square\" inherits from \"rectangle\" and that \"rectangle inherits from convex_polygon.\n",
    "\n",
    "In fact, the inheritance is transitive and so \"square\" inherits from \"convex_polygon\".\n",
    "\n",
    "More precisely, by writing the above code, when python creates an object of type \"square\", it adds automatically the functions defined in the parent classes (parent is chosen as analogy with \"inheritance\") \"square\" and \"convex_polygon\" in the object that it just created.\n",
    "\n",
    "In fact, the implementation of the function \"number_of_corners()\" was factorized in the class \"convex_polygon\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "s = square([1,2,3,4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "s.number_of_corners()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You most probably noticed that the function \"\\_\\_init\\_\\_\" was also factorized.\n",
    "\n",
    "Nevertheless, this is not a good idea to use the same constructor for the square, the rectangle and the convex polygon.\n",
    "Effectively, we want that the square constructor raises an error if the user passes as a parameter more than 4 points.\n",
    "\n",
    "So, we will redefine the function \"\\_\\_init\\_\\_\" in square. We say that we overwrite \"\\_\\_init\\_\\_\".\n",
    "This way, the implementation of \"\\_\\_init\\_\\_\" in convex_polygon will be ignored excepted if we call it explicitely by hand.\n",
    "\n",
    "For example,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class convex_polygon:\n",
    "    def __init__( self, points ):\n",
    "        self.points = points\n",
    "    def number_of_corners(self):\n",
    "        return len(self.points)\n",
    "    \n",
    "class rectangle(convex_polygon):\n",
    "    pass\n",
    "\n",
    "class square(rectangle):\n",
    "    def __init__(self, points):\n",
    "        if len(points) != 4:\n",
    "            raise ValueError(\"Square should contain 4 points.\")\n",
    "        convex_polygon.__init__(self, points )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "s = square([1,2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "s = square([1,2,3,4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "s.points"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, the architecture of how class seems right.\n",
    "\n",
    "This way, as soon as we add a new function in \"convex_polygon\", it will also be available for square."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise**: Well, the architecture is not as good as we say it is. Why? Modify the code to take this into account."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise** : Write a function \"symetry_center(self)\" that returnsthe center of gravity of the objects."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise** : Add a function 'is_inside(self,point)' in square, rectangle and convex_polygon."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "SageMath 7.5.beta3",
   "language": "",
   "name": "sagemath"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
