{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python OOP - Class Static and Instance methods\n",
    "\n",
    "- [Dbader - Method Types in Python OOP: @classmethod, @staticmethod, and Instance Methods](https://www.youtube.com/watch?v=PNpt7cFjGsM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyClass(object):\n",
    "    def method(self):   # access instance and class\n",
    "        return \"instance method called\", self\n",
    "    \n",
    "    @classmethod\n",
    "    def classmethod(cls):  # only access class\n",
    "        return \"class method called\", cls\n",
    "    \n",
    "    @staticmethod\n",
    "    def staticmethod():  # acess nothing\n",
    "        return \"static method called\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Call from **instance**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "m = MyClass()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('instance method called', <__main__.MyClass at 0x104018748>)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m.method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.MyClass"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m.classmethod()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'static method called'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m.staticmethod()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Call from **method**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "method() missing 1 required positional argument: 'self'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-2-88f17b4fa117>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mMyClass\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: method() missing 1 required positional argument: 'self'"
     ]
    }
   ],
   "source": [
    "MyClass.method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('class method called', __main__.MyClass)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "MyClass.classmethod()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'static method called'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "MyClass.staticmethod()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exmple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Pizza(object):\n",
    "    def __init__(self, radius, ingredients):\n",
    "        self.radius = radius\n",
    "        self.ingredients = ingredients\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Pizza({self.ingredients}))'\n",
    "    \n",
    "    @classmethod\n",
    "    def margherita(cls):\n",
    "        return cls(2.5, ['cheese', 'tomatoes'])\n",
    "\n",
    "    @classmethod\n",
    "    def prosciuto(cls):\n",
    "        return cls(3.0, ['cheese', 'tomatoes', 'ham', 'mushrooms'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pizza(['cheese', 'tomatoes']))"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1 = Pizza(1.3, ['cheese', 'tomatoes']); p1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pizza(['cheese', 'tomatoes']))"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# A simplified interface for users\n",
    "Pizza.margherita()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pizza(['cheese', 'tomatoes', 'ham', 'mushrooms']))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Pizza.prosciuto()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### When to use static methods?\n",
    "\n",
    "- Independent, self-contained\n",
    "- Ad-hoc helper functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Pizza(object):\n",
    "    def __init__(self, radius, ingredients):\n",
    "        self.radius = radius\n",
    "        self.ingredients = ingredients\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Pizza({self.ingredients}))'\n",
    "    \n",
    "    @classmethod\n",
    "    def margherita(cls):\n",
    "        return cls(2.5, ['cheese', 'tomatoes'])\n",
    "\n",
    "    @classmethod\n",
    "    def prosciuto(cls):\n",
    "        return cls(3.0, ['cheese', 'tomatoes', 'ham', 'mushrooms'])\n",
    "    \n",
    "    @property\n",
    "    def area(self):\n",
    "        import math\n",
    "        return self.radius ** 2 * math.pi\n",
    "\n",
    "    @staticmethod\n",
    "    def _circle_area(r):  # internal usage\n",
    "        import math\n",
    "        return r ** 2 * math.pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pizza(['cheese', 'tomatoes']))"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1 = Pizza(1.3, ['cheese', 'tomatoes']); p1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.3092915845667505"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p1.area"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3631.681107549801"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Pizza._circle_area(34)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Recap\n",
    "\n",
    "### Instance Method:\n",
    "\n",
    "- ✅ Can modify object instance state\n",
    "- ✅ Can modify class state\n",
    "\n",
    "### Class Method:\n",
    "\n",
    "- 🚫 Can't modify object instance state\n",
    "- ✅ Can modify class state\n",
    "\n",
    "### Static Method:\n",
    "\n",
    "- 🚫 Can't modify object instance state\n",
    "\n",
    "- 🚫 Can't modify class state"
   ]
  }
 ],
 "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.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
