{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## <p style=\"text-align: right;\"> &#9989; Put your name here</p>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day-16-Preclass Introduction to Classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. Class Standard Methods (Fitting in)\n",
    "\n",
    "Here are some things we know about encapsulation as we know it in classes:\n",
    "\n",
    "- classes allow us to hide details from a programmer\n",
    "- classes are modular/portable as they are self-contained\n",
    "- classes provide an *interface* for their use, the methods defined in the class\n",
    "\n",
    "To this list, we want to add the idea of consistency:\n",
    "\n",
    "- a new class should be consistent with the rules and syntax of the language\n",
    "- a new class should respond to standard methods."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.1 Using Standard Methods\n",
    "\n",
    "Nearly every OOP system provides a set of standard methods that the underlying language can utilize. If you look around you can also find these methods named \"magic methods\". They are not magic, but if you add such a method to your class, then the OOP system will utilize it in really useful ways.\n",
    "\n",
    "Understanding these methods is to understand what standard methods the language provides. \n",
    "\n",
    "Here are some things we know about encapsulation as we know it in classes:\n",
    "\n",
    "- classes allow us to hide details from a programmer\n",
    "- classes are modular/portable as they are self-contained\n",
    "- classes provide an *interface* for their use, the methods defined in the class\n",
    "\n",
    "To this list, we want to add the idea of consistency:\n",
    "\n",
    "- a new class should be consistent with the rules and syntax of the language\n",
    "- a new class should respond to standard methods."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2 What are the standard methods\n",
    "\n",
    "Let's look at the contents again of an instance of a standard Python class, the `str` (the string) class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__add__',\n",
       " '__class__',\n",
       " '__contains__',\n",
       " '__delattr__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__getitem__',\n",
       " '__getnewargs__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__iter__',\n",
       " '__le__',\n",
       " '__len__',\n",
       " '__lt__',\n",
       " '__mod__',\n",
       " '__mul__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__rmod__',\n",
       " '__rmul__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " 'capitalize',\n",
       " 'casefold',\n",
       " 'center',\n",
       " 'count',\n",
       " 'encode',\n",
       " 'endswith',\n",
       " 'expandtabs',\n",
       " 'find',\n",
       " 'format',\n",
       " 'format_map',\n",
       " 'index',\n",
       " 'isalnum',\n",
       " 'isalpha',\n",
       " 'isascii',\n",
       " 'isdecimal',\n",
       " 'isdigit',\n",
       " 'isidentifier',\n",
       " 'islower',\n",
       " 'isnumeric',\n",
       " 'isprintable',\n",
       " 'isspace',\n",
       " 'istitle',\n",
       " 'isupper',\n",
       " 'join',\n",
       " 'ljust',\n",
       " 'lower',\n",
       " 'lstrip',\n",
       " 'maketrans',\n",
       " 'partition',\n",
       " 'replace',\n",
       " 'rfind',\n",
       " 'rindex',\n",
       " 'rjust',\n",
       " 'rpartition',\n",
       " 'rsplit',\n",
       " 'rstrip',\n",
       " 'split',\n",
       " 'splitlines',\n",
       " 'startswith',\n",
       " 'strip',\n",
       " 'swapcase',\n",
       " 'title',\n",
       " 'translate',\n",
       " 'upper',\n",
       " 'zfill']"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_str = str()\n",
    "dir(my_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "All those methods that begin and end with two underline characters, such as `__init__` or `__str__` are some of the standard methods. These methods are setup such that Python looks to call a standard method in a user-defined class under the appropriate circumstances. For example:\n",
    "- `__init__` is called when a class instance is contructed/initialized\n",
    "- `__str__` is called when a string representation for an instance is required.\n",
    "\n",
    "There are quite a few of such methods. There are a whole set of Boolean comparison methods: `__le__`, `__lt__`, `__ge__`, `__gt__`, `__eq__`, `__ne__` meaning that there are methods for (respectively) less than or equal, less than, greater than or equal, greater than, equal, and not equal.\n",
    "\n",
    "There are arithmetic operators: `__add__` and `__mul__` for add and multiply. Though strings lack them, there could also be methods for subtraction, division, etc.\n",
    "\n",
    "Defining these methods for our class allows us to \"fit in\" to the existing structure of Python and have Python utilize our class more effectively"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. Point Class\n",
    "\n",
    "Back to our point class. We have already discussed `__init__` which gets called when we create an instance. We have added below `__str__` which gets called in the context of creating a string from the instance. We have added some print statements to our `__init__` and `__str__` so that we see when they get called.\n",
    "\n",
    "What's really nice about this is that, because we fit in with what Python expects, we get behavior we hadn't even planned on, namely printing works because we defined `__str__`\n",
    "\n",
    "Note the `__str__` method **has to** return a string. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Point(object):\n",
    "   def __init__(self,x=0,y=0):   \n",
    "        self.x_coord = x\n",
    "        self.y_coord = y\n",
    "        print(\"Init called\")\n",
    "   def __str__(self):\n",
    "        print(\"String called\")\n",
    "        return \"({:02d}:{:02d})\".format(self.x_coord, self.y_coord)\n",
    "   def translate(self,x=0,y=0): \n",
    "        self.x_coord += x\n",
    "        self.y_coord += y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Init called\n",
      "String called\n",
      "(01:02)\n",
      "String called\n",
      "(01:02)\n"
     ]
    }
   ],
   "source": [
    "my_point = Point(1,2)\n",
    "print(my_point)      # implicitly call the __str__ method\n",
    "a_str = str(my_point)# directly call the __str__ method\n",
    "print(a_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When we call the constructor, Python calls the `__init__` method we have defined. When we call a `print` of our class instance, Python knows to search for a way to turn the instance into a string. If such a method, `__str__`, exists it calls it and prints that string!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1 The `dir` function again\n",
    "Let's look a the dir again of a point instance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "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",
       " 'translate',\n",
       " 'x_coord',\n",
       " 'y_coord']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(my_point)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A lot of standard methods are defined even though we didn't do it. However, some work and some don't"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Point'>\n"
     ]
    }
   ],
   "source": [
    "print(my_point.__class__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'<' not supported between instances of 'Point' and 'Point'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-7-2bfff3b42539>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_point\u001b[0m \u001b[1;33m<\u001b[0m \u001b[0mmy_point\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: '<' not supported between instances of 'Point' and 'Point'"
     ]
    }
   ],
   "source": [
    "my_point < my_point"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a list of all the potential standard methods you could use in the Python class system.\n",
    "\n",
    "<img src=\"https://i.ibb.co/ygB6JGc/standard-methods.png\" alt=\"standard-methods\" border=\"0\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Question** Which methods do you suppose represent the `==` comparison operator?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font size=8 color=\"#009600\">&#9998;</font> Your answer here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Question** There is a method `__repr__` that all instances have. What does it do? When is is called? Look it up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font size=8 color=\"#009600\">&#9998;</font> Your answer here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. Class variables\n",
    "\n",
    "So far, we have seen that we can assign any value to an instance using a dot call, for example:\n",
    "\n",
    "`my_point.new_attribute = 42`\n",
    "\n",
    "Creates the variable `new_attribute` inside of the `my_point` instance and stores the value 42 there. We use the `__init__` method as a way to assign attribtutes to **every** instance when it gets made. Each instance has its own copy of the attribute and each can be changed individually."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.1 Assign attribute to a class\n",
    "\n",
    "So, if we can do dot assignment to an instance, can we do it to a class? Let's try"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n"
     ]
    }
   ],
   "source": [
    "Point.class_attribute = 42\n",
    "print(Point.class_attribute)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But what does that mean to all the instances? "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.2 Find the attribute, round 1\n",
    "\n",
    "Python has a very easy way it uses to try and find an attribute when a dot call is made. It's a 3 step process, let's look at the first two:\n",
    "\n",
    "1. Look in the instance. If the attribute is in the instance (data or method) return it.\n",
    "2. If the attribute is not in the instance, **look in the class** of the instance. If it is stored in the class, return it.\n",
    "\n",
    "How does an instance remember what class it is associated with? \n",
    "\n",
    "Look at the `dir` of a point instance above. There is a data attribute called `__class__` which is the class from which the instance is derived. It is created automatically when the instance is created and set to the class itself. Thus:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Point'>\n"
     ]
    }
   ],
   "source": [
    "print(my_point.__class__)   # prints the Point class"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I can also now do the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n"
     ]
    }
   ],
   "source": [
    "print(my_point.class_attribute)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The attribute `class_attribute` is not part of the instance, but following the \"find the attribute\" rule we look to the instance's class as a second step. We find the attribute in the class and return it.\n",
    "\n",
    "What happens when we do the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Init called\n",
      "123\n",
      "42\n"
     ]
    }
   ],
   "source": [
    "new_point = Point(2,2)\n",
    "new_point.class_attribute = 123\n",
    "print(new_point.class_attribute)\n",
    "print(my_point.class_attribute)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Question** Can you explain what happened to give those values (hint, dot assignment goes to the instance)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font size=8 color=\"#009600\">&#9998;</font> Your answer here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Class variables are useful to store default values or global values you want to be available to all instances of a class. We will find use for them a little later. Adding to our `Point` class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Point(object):\n",
    "   class_variable = 123\n",
    "   def __init__(self,x=0,y=0):   \n",
    "        self.x_coord = x\n",
    "        self.y_coord = y\n",
    "        print(\"Init called\")\n",
    "   def __str__(self):\n",
    "        print(\"String called\")\n",
    "        return \"({:02d}:{:02d})\".format(self.x_coord, self.y_coord)\n",
    "   def translate(self,x=0,y=0): \n",
    "        self.x_coord += x\n",
    "        self.y_coord += y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The attribute `class_variable` is created as part of the class and can be accessed by all instances (assuming that the instance doesn't make it's own local copy)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Init called\n",
      "123\n"
     ]
    }
   ],
   "source": [
    "my_point= Point(3,3)\n",
    "print(my_point.class_variable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Assignment wrap-up\n",
    "\n",
    "Please fill out the form that appears when you run the code below.  **You must completely fill this out in order to receive credit for the assignment!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import HTML\n",
    "HTML(\n",
    "\"\"\"\n",
    "<iframe \n",
    "\tsrc=\"https://cmse.msu.edu/cmse202-pc-survey\" \n",
    "\twidth=\"800px\" \n",
    "\theight=\"600px\" \n",
    "\tframeborder=\"0\" \n",
    "\tmarginheight=\"0\" \n",
    "\tmarginwidth=\"0\">\n",
    "\tLoading...\n",
    "</iframe>\n",
    "\"\"\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---------\n",
    "### Congratulations, you're done with your pre-class assignment!\n",
    "\n",
    "Now, you just need to submit this assignment by uploading it to the course <a href=\"https://d2l.msu.edu/\">Desire2Learn</a> web page for today's submission folder (Don't forget to add your names in the first cell).\n",
    "\n",
    "\n",
    "&#169; Copyright Michigan State University Board of Trustees"
   ]
  }
 ],
 "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.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
