{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction to Python  \n",
    "\n",
    "## [Classes](https://docs.python.org/3/tutorial/classes.html) and OOP\n",
    "\n",
    "\n",
    "+ Classes provide means of bundling data and functionality together.  \n",
    "+ Creating a new class is equivalent to creating a new type of object, allowing new instances of that type to be made.  \n",
    "+ Each class instance can have attributes attached to it for maintaining its state.  \n",
    "+ Class instances can also have methods for modifying its state.  \n",
    "\n",
    "#### The class signature is:\n",
    "\n",
    "    class ClassName(Superclass - optional):\n",
    "        <statement-1>\n",
    "        .\n",
    "        .\n",
    "        .\n",
    "        <statement-N>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's start with an example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyClass:\n",
    "    \"\"\"\n",
    "    class documentation string\n",
    "    \"\"\"\n",
    "    \n",
    "    class_attribute1 = 1234\n",
    "    class_attribute2 = 5678\n",
    "    \n",
    "    def class_method(self):\n",
    "        return 'Hello!'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'type'>\n"
     ]
    }
   ],
   "source": [
    "print(type(MyClass))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = MyClass()\n",
    "y = MyClass()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.MyClass'>\n"
     ]
    }
   ],
   "source": [
    "print(type(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.MyClass'>\n"
     ]
    }
   ],
   "source": [
    "print(type(y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'class_attribute1',\n",
       " 'class_attribute2',\n",
       " 'class_method']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8754036884727\n",
      "8754036884538\n"
     ]
    }
   ],
   "source": [
    "print(x.__hash__())\n",
    "print(y.__hash__())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1234"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.class_attribute1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5678"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.class_attribute2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello!'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.class_method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.class_attribute1 = 4321"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4321"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.class_attribute1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1234"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y.class_attribute1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.new_attribute = \"any new attribute\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = [1,2,3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'class_attribute1',\n",
       " 'class_attribute2',\n",
       " 'class_method',\n",
       " 'new_attribute']"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'class_attribute1',\n",
       " 'class_attribute2',\n",
       " 'class_method']"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Another example, with __init__() method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyComplex:\n",
    "    def __init__(self, realpart, imagpart):\n",
    "        self.r = realpart\n",
    "        self.i = imagpart\n",
    "        print(\"__init__ has run\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__init__ has run\n"
     ]
    }
   ],
   "source": [
    "z = MyComplex(2,-4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__init__ has run\n"
     ]
    }
   ],
   "source": [
    "j = MyComplex(13,12)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-4"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'i',\n",
       " 'r']"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(j)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Adding methods:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Rectangle:\n",
    "    def __init__(self, width, height):\n",
    "        self.width = width\n",
    "        self.height = height\n",
    "        self.area = width * height\n",
    "        self.perimeter = 2 * (width+height)\n",
    "        if width < height:\n",
    "            self.invert_sides()\n",
    "            \n",
    "    def invert_sides(self):\n",
    "        self.width, self.height = self.height, self.width\n",
    "        print('inverted height and width')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_rectangle = Rectangle(50,10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "print(my_rectangle.width)\n",
    "print(my_rectangle.height)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "500\n",
      "120\n"
     ]
    }
   ],
   "source": [
    "print(my_rectangle.area)\n",
    "print(my_rectangle.perimeter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inverted height and width\n"
     ]
    }
   ],
   "source": [
    "my_rectangle.invert_sides()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "50\n"
     ]
    }
   ],
   "source": [
    "print(my_rectangle.width)\n",
    "print(my_rectangle.height)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'area',\n",
       " 'height',\n",
       " 'invert_sides',\n",
       " 'perimeter',\n",
       " 'width']"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(my_rectangle)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### What is printed when we call the \"print\" primitive in an object?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'<__main__.Rectangle object at 0x7fc39c6df990>'"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_rectangle.__repr__()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### _print()_ calls the internal __repr__() method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Rectangle object at 0x7f6354250bb0>\n"
     ]
    }
   ],
   "source": [
    "print(my_rectangle)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Another class example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Triangle:\n",
    "    def __init__(self, side1, side2, side3):\n",
    "        self.side1 = side1\n",
    "        self.side2 = side2\n",
    "        self.side3 = side3\n",
    "        print('running __init__\\n')\n",
    "        self.type_of_triangle()\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f\"I am a Triangle with sides {self.side1}, {self.side2} and {self.side3}\"\n",
    "        \n",
    "    #def __repr__(self):\n",
    "    #    return \"42\"\n",
    "        \n",
    "    def type_of_triangle(self):\n",
    "        if self.side1 == self.side2 and self.side1 == self.side3:\n",
    "            print('I am equilateral')\n",
    "            self.mytype = 'equilateral'\n",
    "        elif self.side1 == self.side2 or \\\n",
    "             self.side1 == self.side3 or \\\n",
    "             self.side2 == self.side3:\n",
    "            print('I am isosceles')\n",
    "            self.mytype = 'isosceles'\n",
    "        else:\n",
    "            print('I am scalene')\n",
    "            self.mytype = 'scalene'\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "running __init__\n",
      "\n",
      "I am equilateral\n"
     ]
    }
   ],
   "source": [
    "tri = Triangle(5,5,5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "running __init__\n",
      "\n",
      "I am isosceles\n"
     ]
    }
   ],
   "source": [
    "tri.__init__(5,4,5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "isosceles\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "print(tri.mytype)\n",
    "print(tri.side1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I am isosceles\n"
     ]
    }
   ],
   "source": [
    "tri.type_of_triangle()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I am a Triangle with sides 5, 4 and 5\n"
     ]
    }
   ],
   "source": [
    "print(tri)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " 'mytype',\n",
       " 'side1',\n",
       " 'side2',\n",
       " 'side3',\n",
       " 'type_of_triangle']"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(tri)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [Special Methods for Classes](https://docs.python.org/3/reference/datamodel.html#special-method-names)  \n",
    "\n",
    "[examples](https://www.pythonlikeyoumeanit.com/Module4_OOP/Special_Methods.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "x[key] = item<table class=\"docutils align-default\">\n",
    "<colgroup>\n",
    "<col style=\"width: 33%\">\n",
    "<col style=\"width: 33%\">\n",
    "<col style=\"width: 33%\">\n",
    "</colgroup>\n",
    "<thead>\n",
    "<tr class=\"row-odd\"><th class=\"head\"><p>Method</p></th>\n",
    "<th class=\"head\"><p>Signature</p></th>\n",
    "<th class=\"head\"><p>Explanation</p></th>\n",
    "</tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr class=\"row-even\"><td><p>Returns string for a printable representation of object</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__repr__(self)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">repr(x)</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__repr__()</span></code>, this is also invoked when an object is returned by a console</p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Returns string representation of an object</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__str__(self)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">str(x)</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__str__()</span></code></p></td>\n",
    "</tr>\n",
    "</tbody>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table class=\"docutils align-default\">\n",
    "<colgroup>\n",
    "<col style=\"width: 13%\">\n",
    "<col style=\"width: 38%\">\n",
    "<col style=\"width: 50%\">\n",
    "</colgroup>\n",
    "<thead>\n",
    "<tr class=\"row-odd\"><th class=\"head\"><p>Method</p></th>\n",
    "<th class=\"head\"><p>Signature</p></th>\n",
    "<th class=\"head\"><p>Explanation</p></th>\n",
    "</tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr class=\"row-even\"><td><p>Add</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__add__(self,</span> <span class=\"pre\">other)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x</span> <span class=\"pre\">+</span> <span class=\"pre\">y</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__add__(y)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Subtract</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__sub__(self,</span> <span class=\"pre\">other)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x</span> <span class=\"pre\">-</span> <span class=\"pre\">y</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__sub__(y)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-even\"><td><p>Multiply</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__mul__(self,</span> <span class=\"pre\">other)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x</span> <span class=\"pre\">*</span> <span class=\"pre\">y</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__mul__(y)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Divide</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__truediv__(self,</span> <span class=\"pre\">other)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x</span> <span class=\"pre\">/</span> <span class=\"pre\">y</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__truediv__(y)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-even\"><td><p>Power</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__pow__(self,</span> <span class=\"pre\">other)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x</span> <span class=\"pre\">**</span> <span class=\"pre\">y</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__pow__(y)</span></code></p></td>\n",
    "</tr>\n",
    "</tbody>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table class=\"docutils align-default\">\n",
    "<colgroup>\n",
    "<col style=\"width: 33%\">\n",
    "<col style=\"width: 33%\">\n",
    "<col style=\"width: 33%\">\n",
    "</colgroup>\n",
    "<thead>\n",
    "<tr class=\"row-odd\"><th class=\"head\"><p>Method</p></th>\n",
    "<th class=\"head\"><p>Signature</p></th>\n",
    "<th class=\"head\"><p>Explanation</p></th>\n",
    "</tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr class=\"row-even\"><td><p>Length</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__len__(self)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">len(x)</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__len__()</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Get Item</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__getitem__(self,</span> <span class=\"pre\">key)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x[key]</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__getitem__(key)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-even\"><td><p>Set Item</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__setitem__(self,</span> <span class=\"pre\">key,</span> <span class=\"pre\">item)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">x[key]</span> <span class=\"pre\">=</span> <span class=\"pre\">item</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__setitem__(key,</span> <span class=\"pre\">item)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Contains</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__contains__(self,</span> <span class=\"pre\">item)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">item</span> <span class=\"pre\">in</span> <span class=\"pre\">x</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__contains__(item)</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-even\"><td><p>Iterator</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__iter__(self)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">iter(x)</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__iter__()</span></code></p></td>\n",
    "</tr>\n",
    "<tr class=\"row-odd\"><td><p>Next</p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">__next__(self)</span></code></p></td>\n",
    "<td><p><code class=\"docutils literal notranslate\"><span class=\"pre\">next(x)</span></code> invokes <code class=\"docutils literal notranslate\"><span class=\"pre\">x.__next__()</span></code></p></td>\n",
    "</tr>\n",
    "</tbody>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Operator overloading __gt__ (>) and __lt__ (<)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Thing:\n",
    "    def __init__(self, value):\n",
    "        self.__value = value\n",
    "        \n",
    "    def __gt__(self, other):\n",
    "        return self.__value > other.__value\n",
    "    \n",
    "    def __lt__(self, other):\n",
    "        return self.__value < other.__value\n",
    "    \n",
    "something = Thing(100)\n",
    "nothing = Thing(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "something > nothing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "something < nothing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "unsupported operand type(s) for +: 'Thing' and 'Thing'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-43-5fbac983769e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msomething\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnothing\u001b[0m \u001b[0;31m# erro\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'Thing' and 'Thing'"
     ]
    }
   ],
   "source": [
    "something + nothing # erro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Thing:\n",
    "    def __init__(self, value):\n",
    "        self.__value = value\n",
    "        \n",
    "    def __gt__(self, other):\n",
    "        return self.__value > other.__value\n",
    "    \n",
    "    def __lt__(self, other):\n",
    "        return self.__value < other.__value\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        return self.__value + other.__value\n",
    "    \n",
    "something = Thing(100)\n",
    "nothing = Thing(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "110"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "something + nothing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "110"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nothing + something"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [Class inheritance](https://medium.com/swlh/classes-subclasses-in-python-12b6013d9f3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog:\n",
    "    def __init__(self, name, race='Royal Street Dog'):\n",
    "        self.name = name\n",
    "        self.race = race\n",
    "        self.tricks = []\n",
    "\n",
    "    def add_trick(self, trick):\n",
    "        if not trick in self.tricks:\n",
    "            self.tricks.append(trick.title())\n",
    "        else:\n",
    "            print('I have learnt that already!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = Dog('Fido','German Shepherd')\n",
    "e = Dog('Buddy','Cocker')\n",
    "f = Dog('Rex')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fido\n",
      "German Shepherd\n",
      "Buddy\n",
      "Cocker\n",
      "Rex\n",
      "Royal Street Dog\n"
     ]
    }
   ],
   "source": [
    "print(d.name)\n",
    "print(d.race)\n",
    "print(e.name)\n",
    "print(e.race)\n",
    "print(f.name)\n",
    "print(f.race)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "d.add_trick('Roll')\n",
    "d.add_trick('Pretending dead')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Roll', 'Pretending Dead']"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "d.add_trick('Ask for food')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Roll', 'Pretending Dead', 'Ask For Food']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e.tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Bark']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.add_trick('Bark')\n",
    "f.tricks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating a subclass using inheritance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "class SuperDog(Dog):\n",
    "    \n",
    "    def __init__(self, name, race):\n",
    "        Dog.__init__(self, name, race)\n",
    "        self.food = False\n",
    "        self.trained = False\n",
    "        self.last_meal = time.time()\n",
    "        \n",
    "    def is_hungry(self):\n",
    "        if time.time() - self.last_meal < 20:\n",
    "            print('Not hungry')\n",
    "        else:\n",
    "            print(f'Yes, my last meal was {(time.time() - self.last_meal)/60:.2f} min. ago')\n",
    "    \n",
    "    def train(self):\n",
    "        self.trained = True\n",
    "    \n",
    "    def feed(self):\n",
    "        self.last_meal = time.time()\n",
    "        print('The food was delicious. Thanks!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = SuperDog('Raghu','Labrador')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Not hungry\n"
     ]
    }
   ],
   "source": [
    "f.is_hungry()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Yes, my last meal was 0.44 min. ago\n"
     ]
    }
   ],
   "source": [
    "f.is_hungry() #calling after some time..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The food was delicious. Thanks!\n"
     ]
    }
   ],
   "source": [
    "f.feed()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Not hungry\n"
     ]
    }
   ],
   "source": [
    "f.is_hungry()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.add_trick('Give Five')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Give Five']"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.tricks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.trained"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.train()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.trained"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [Multiple inheritance](https://www.techbeamers.com/python-multiple-inheritance/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " In class B\n"
     ]
    }
   ],
   "source": [
    "class A: \n",
    "    def rk(self): \n",
    "        print(\" In class A\") \n",
    "        \n",
    "class B(A): \n",
    "    def rk(self): \n",
    "        print(\" In class B\") \n",
    "        \n",
    "class C(A): \n",
    "    def rk(self): \n",
    "        print(\"In class C\") \n",
    "  \n",
    "# classes ordering \n",
    "class D(B, C): \n",
    "    pass\n",
    "     \n",
    "r = D() \n",
    "r.rk() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Another example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal:\n",
    "    def __init__(self, weight=0, height=0):\n",
    "        self.weight = weight\n",
    "        self.height = height"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Carnivore(Animal):\n",
    "    def __init__(self, weight=0, height=0):\n",
    "        super().__init__(weight, height)\n",
    "\n",
    "    def say(self):\n",
    "        raise NotImplementedError"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2000"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "TRex = Carnivore(2000)\n",
    "TRex.weight"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "TRex.height"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Wolf(Carnivore):\n",
    "    def __init__(self, weight, height):\n",
    "        super().__init__(weight, height)\n",
    "        \n",
    "    def say(self):\n",
    "        #print(\"Bark! Bark!\")\n",
    "        return \"Bark! Bark!\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n",
      "45\n",
      "Bark! Bark!\n"
     ]
    }
   ],
   "source": [
    "Jack = Wolf(8, 45)\n",
    "print(Jack.weight)\n",
    "print(Jack.height)\n",
    "print(Jack.say())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Pet(Animal):\n",
    "    def __init__(self, tutor, weight=0, height=0):\n",
    "        super().__init__(weight, height)\n",
    "        self.tutor = tutor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "John\n",
      "35\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "fish = Pet('John', 35)\n",
    "\n",
    "print(fish.tutor)\n",
    "print(fish.weight)\n",
    "print(fish.height)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat(Carnivore, Pet):\n",
    "    def __init__(self, weight, height, tutor):\n",
    "        super(Carnivore, self).__init__(self, weight, height)\n",
    "        print(self.weight)\n",
    "        print(self.height)\n",
    "        Pet.__init__(self, tutor, self.weight, self.height)\n",
    "        print(self.tutor)\n",
    "        \n",
    "    def say(self):\n",
    "        print(\"Meaw!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[C3 & MRO](https://en.wikipedia.org/wiki/C3_linearization)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(__main__.Cat, __main__.Carnivore, __main__.Pet, __main__.Animal, object)"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Cat.__mro__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n",
      "25\n",
      "John\n"
     ]
    }
   ],
   "source": [
    "Garfield = Cat(4, 25, 'John')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Weight: 4\n",
      "Height: 25\n",
      "Tutor: John\n",
      "Meaw!\n"
     ]
    }
   ],
   "source": [
    "print(f\"Weight: {Garfield.weight}\\nHeight: {Garfield.height}\\nTutor: {Garfield.tutor}\")\n",
    "Garfield.say()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modifying an existing class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyInteger(int):\n",
    "    def __init__(self,number):\n",
    "        super().__init__() \n",
    "    \n",
    "    def __add__(self, other):\n",
    "        return self * other\n",
    "    \n",
    "    def __sub__(self, other):\n",
    "        return self + other\n",
    "    \n",
    "    def square(self):\n",
    "        return self * self"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = MyInteger(2)\n",
    "b = MyInteger(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__abs__',\n",
       " '__add__',\n",
       " '__and__',\n",
       " '__bool__',\n",
       " '__ceil__',\n",
       " '__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__divmod__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__float__',\n",
       " '__floor__',\n",
       " '__floordiv__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__getnewargs__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__index__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__int__',\n",
       " '__invert__',\n",
       " '__le__',\n",
       " '__lshift__',\n",
       " '__lt__',\n",
       " '__mod__',\n",
       " '__module__',\n",
       " '__mul__',\n",
       " '__ne__',\n",
       " '__neg__',\n",
       " '__new__',\n",
       " '__or__',\n",
       " '__pos__',\n",
       " '__pow__',\n",
       " '__radd__',\n",
       " '__rand__',\n",
       " '__rdivmod__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__rfloordiv__',\n",
       " '__rlshift__',\n",
       " '__rmod__',\n",
       " '__rmul__',\n",
       " '__ror__',\n",
       " '__round__',\n",
       " '__rpow__',\n",
       " '__rrshift__',\n",
       " '__rshift__',\n",
       " '__rsub__',\n",
       " '__rtruediv__',\n",
       " '__rxor__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__sub__',\n",
       " '__subclasshook__',\n",
       " '__truediv__',\n",
       " '__trunc__',\n",
       " '__xor__',\n",
       " 'as_integer_ratio',\n",
       " 'bit_length',\n",
       " 'conjugate',\n",
       " 'denominator',\n",
       " 'from_bytes',\n",
       " 'imag',\n",
       " 'numerator',\n",
       " 'real',\n",
       " 'square',\n",
       " 'to_bytes']"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a * b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a + b  #?????"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a - b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.square()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.square()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 220,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on wrapper_descriptor:\n",
      "\n",
      "__mul__(self, value, /)\n",
      "    Return self*value.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(MyInteger.__mul__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 221,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on wrapper_descriptor:\n",
      "\n",
      "__str__(self, /)\n",
      "    Return str(self).\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(MyInteger.__str__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A more complete example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "def strike(text):\n",
    "    \"\"\" Renders string with strike-through characters through it.\n",
    "\n",
    "        `strike('hello world')` -> '̶h̶e̶l̶l̶o̶ ̶w̶o̶r̶l̶d'\n",
    "\n",
    "        Notes\n",
    "        -----\n",
    "        \\u0336 is a special strike-through unicode character; it\n",
    "        is not unique to Python.\"\"\"\n",
    "    return ''.join('\\u0336{}'.format(c) for c in text)\n",
    "\n",
    "class ShoppingList:\n",
    "    def __init__(self, items):\n",
    "        self._needed = set(items)\n",
    "        self._purchased = set()\n",
    "\n",
    "    def __repr__(self):\n",
    "        \"\"\" Returns formatted shopping list as a string with\n",
    "            purchased items being crossed out.\n",
    "\n",
    "            Returns\n",
    "            -------\n",
    "            str\"\"\"\n",
    "        if self._needed or self._purchased:\n",
    "            remaining_items = [str(i) for i in self._needed]\n",
    "            purchased_items = [strike(str(i)) for i in self._purchased]\n",
    "            # You wont find the • character on your keyboard. I simply\n",
    "            # googled \"unicode bullet point\" and copied/pasted it here.\n",
    "            return \"• \" + \"\\n• \".join(remaining_items + purchased_items)\n",
    "\n",
    "    def add_new_items(self, items):\n",
    "        self._needed.update(items)\n",
    "\n",
    "    def mark_purchased_items(self, items):\n",
    "        self._purchased.update(set(items) & self._needed)\n",
    "        self._needed.difference_update(self._purchased)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "• flour\n",
      "• salt\n",
      "• eggs\n",
      "• milk\n"
     ]
    }
   ],
   "source": [
    "food = ShoppingList([\"milk\", \"flour\", \"salt\", \"eggs\"])\n",
    "print(food)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "• eggs\n",
      "• milk\n",
      "• ̶f̶l̶o̶u̶r\n",
      "• ̶s̶a̶l̶t\n"
     ]
    }
   ],
   "source": [
    "food.mark_purchased_items([\"flour\", \"salt\"])\n",
    "print(food)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "def __add__(self, other):\n",
    "    \"\"\" Add the unpurchased and purchased items from another shopping\n",
    "        list to the present one.\n",
    "\n",
    "        Parameters\n",
    "        ----------\n",
    "        other : ShoppingList\n",
    "            The shopping list whose items we will add to the present one.\n",
    "        Returns\n",
    "        -------\n",
    "        ShoppingList\n",
    "            The present shopping list, with items added to it.\"\"\"\n",
    "    new_list = ShoppingList([])\n",
    "    # populate new_list with items from `self` and `other`\n",
    "    for l in [self, other]:\n",
    "        new_list.add_new_items(l._needed)\n",
    "\n",
    "        # add purchased items to list, then mark as purchased\n",
    "        new_list.add_new_items(l._purchased)\n",
    "        new_list.mark_purchased_items(l._purchased)\n",
    "    return new_list\n",
    "\n",
    "# set `__add__` as a method of `ShoppingList`\n",
    "setattr(ShoppingList, \"__add__\", __add__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "• t-shirts\n",
       "• pens\n",
       "• staples\n",
       "• socks\n",
       "• eggs\n",
       "• milk\n",
       "• ̶f̶l̶o̶u̶r\n",
       "• ̶s̶a̶l̶t\n",
       "• ̶p̶e̶n̶c̶i̶l̶s"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "food = ShoppingList([\"milk\", \"flour\", \"salt\", \"eggs\"])\n",
    "food.mark_purchased_items([\"flour\", \"salt\"])\n",
    "\n",
    "office_supplies = ShoppingList([\"staples\", \"pens\", \"pencils\"])\n",
    "office_supplies.mark_purchased_items([\"pencils\"])\n",
    "\n",
    "clothes = ShoppingList([\"t-shirts\", \"socks\"])\n",
    "\n",
    "# combine all three shopping lists\n",
    "food + office_supplies + clothes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyList:\n",
    "    def __init__(self, *args):\n",
    "        if len(args) == 1 and hasattr(args[0], '__iter__'):\n",
    "            # handles `MyList([1, 2, 3])\n",
    "            self._data = list(args[0])\n",
    "        else:\n",
    "            # handles `MyList(1, 2, 3)`\n",
    "            self._data = list(args)\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        out = self._data[index]\n",
    "        # slicing should return a `MyList` instance\n",
    "        # otherwise, the individual element should be returned as-is\n",
    "        return MyList(out) if isinstance(index, slice) else out\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        self._data[key] = value\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self._data)\n",
    "\n",
    "    def __repr__(self):\n",
    "        \"\"\" Use the character | as the delimiter for our list\"\"\"\n",
    "        # `self._data.__repr__()` returns '[ ... ]',\n",
    "        # thus we can slice to get the contents of the string\n",
    "        # and exclude the square-brackets, and add our own\n",
    "        # delimiters in their place\n",
    "        return \"|\" + self._data.__repr__()[1:-1] + \"|\"\n",
    "\n",
    "    def __contains__(self, item):\n",
    "        return item in self._data\n",
    "\n",
    "    def append(self, item):\n",
    "        self._data.append(item)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "|'h', 'e', 'l', 'l', 'o'|"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# MyList can accept any iterable as its\n",
    "# first (and only) input argument\n",
    "x = MyList(\"hello\")\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "|1, 2, 3, 4, 5|"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# MyList accepts an arbitrary number of arguments\n",
    "x = MyList(1, 2, 3, 4, 5)\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "print(len(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# getting an item\n",
    "x[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "|3, 4|"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# slicing returns a MyList instance\n",
    "x[2:4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "|-1, 2, 3, 4, 5|"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# setting an item\n",
    "x[0] = -1\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# checking membership\n",
    "10 in x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "||"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "MyList()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}