{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Class, Object, Instance/Static Variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Date object at 0x7f13e8190438>\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "class Date():\n",
    "    pass\n",
    "\n",
    "# Declare object `d` with class `Date`\n",
    "d = Date()\n",
    "# Add attibutes (instance variables) to object `b`\n",
    "d.year = 2019\n",
    "d.month = 5\n",
    "d.day = 7\n",
    "\n",
    "print(d)  # No print method defined for class `Date`\n",
    "print(d.day)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A Better Way to Define a Class\n",
    "\n",
    "- Class\n",
    "    - Static Variables\n",
    "    - Static Functions (`@staticmethod`)\n",
    "    - Constructor (Special Function): `__init__()`\n",
    "    - Print Method (Special Function): `__str__()`\n",
    "    - Object\n",
    "        - Instance Variables\n",
    "        - Instance Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Date():\n",
    "    ############ Static (Belong to Class) ############\n",
    "    # Static variable\n",
    "    doubleDigit = True\n",
    "    \n",
    "    @staticmethod\n",
    "    def setDoubleDigit(isDouble=True):\n",
    "        Date.doubleDigit = isDouble\n",
    "    \n",
    "    # Constructor\n",
    "    def __init__(self, year, month, day):\n",
    "        self.year = year\n",
    "        self.month = month\n",
    "        self.day = day\n",
    "    \n",
    "    # Print method\n",
    "    def __str__(self):\n",
    "        if Date.doubleDigit == False:\n",
    "            return str(self.year) + \"/\" + str(self.month) + \"/\" + str(self.day)\n",
    "        else:\n",
    "            dateStr = str(self.year) + \"/\"\n",
    "            dateStr += \"0\" + str(self.month) if self.month < 10 else str(self.month)\n",
    "            dateStr += \"/\"\n",
    "            dateStr += \"0\" + str(self.day) if self.day < 10 else str(self.day)\n",
    "            return dateStr\n",
    "\n",
    "    ############ Instances (Belong to Objects) ############\n",
    "    def isLeap(self):\n",
    "        if(self.year % 400 == 0):\n",
    "            return True\n",
    "        elif((self.year % 4 == 0) and (self.year % 100 != 0)):\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "    \n",
    "    def isValidDate(self): # the invoker is a Date object\n",
    "        if((1 <= self.year <= 3000) and (1 <= self.month <= 12)):\n",
    "            daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n",
    "        if(self.isLeap() == True):\n",
    "            daysInMonth[1] = 29\n",
    "        if(1 <= self.day <= daysInMonth[self.month - 1]):\n",
    "            return True\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019/05/07\n",
      "2019/5/7\n",
      "False True\n"
     ]
    }
   ],
   "source": [
    "d = Date(2019, 5, 7)\n",
    "print(d)\n",
    "Date.setDoubleDigit(False)\n",
    "print(d)\n",
    "print(d.isLeap(), d.isValidDate())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "d2 = Date(2019, 5, 33)\n",
    "print(d2.isValidDate())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Advanced Topics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Magic Methods\n",
    "\n",
    "- `__init__`, `__str__`, `__eq__`, `__lt__`, `__gt__`, `__add__`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Protected Attributes, getter & setters\n",
    "\n",
    "<https://medium.com/bryanyang0528/python-setter-和-getter-6c08a9d37d46>"
   ]
  }
 ],
 "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"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}