12
This commit is contained in:
		
							parent
							
								
									f3e3993893
								
							
						
					
					
						commit
						d9456ce2cd
					
				
					 1 changed files with 929 additions and 0 deletions
				
			
		
							
								
								
									
										929
									
								
								12.inheritance.ipynb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										929
									
								
								12.inheritance.ipynb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,929 @@ | |||
| { | ||||
|  "cells": [ | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "44ca09f7", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "## Inheritance\n", | ||||
|     "\n", | ||||
|     "### Motivations\n", | ||||
|     "\n", | ||||
|     "Let's say we're building an application that tracks students." | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 9, | ||||
|    "id": "feaa69a2", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "class Student:\n", | ||||
|     "\n", | ||||
|     "    # this is a class-level variable\n", | ||||
|     "    # instead of each instance having its own copy\n", | ||||
|     "    # the variable is shared among all `Student`\n", | ||||
|     "    next_id_counter = 1\n", | ||||
|     "    \n", | ||||
|     "    def __init__(self, name):\n", | ||||
|     "        # assign each student a unique id\n", | ||||
|     "        # note use of Student. not self.\n", | ||||
|     "        self.id = Student.next_id_counter\n", | ||||
|     "        Student.next_id_counter += 1\n", | ||||
|     "        \n", | ||||
|     "        self.name = name\n", | ||||
|     "        self.year = 1\n", | ||||
|     "        self.major = \"Undeclared\"\n", | ||||
|     "        self.course_grades = {}\n", | ||||
|     "        \n", | ||||
|     "    def add_grade(self, course_name, grade):\n", | ||||
|     "        self.course_grades[course_name] = grade\n", | ||||
|     "    \n", | ||||
|     "    @property\n", | ||||
|     "    def gpa(self):\n", | ||||
|     "        grade_pts = {\"A\":4.0, \"A-\":3.7, \"B+\":3.3, \"B\":3.0, \"B-\":2.7, \"C+\":2.3, \"C\":2.0, \"C-\":1.7, \"D+\":1.3, \"D\":1.0, \"F\":0.0} \n", | ||||
|     "        if len(self.course_grades) == 0:\n", | ||||
|     "            return 0\n", | ||||
|     "        return sum(grade_pts[g] for g in self.course_grades.values()) / len(self.course_grades)\n", | ||||
|     "    \n", | ||||
|     "    def __repr__(self):\n", | ||||
|     "        return f\"Student(name={self.name}, id={self.id}, gpa={self.gpa})\"" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 10, | ||||
|    "id": "c410fb1d", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "s1 = Student(\"Adam\")\n", | ||||
|     "s2 = Student(\"Beth\")\n", | ||||
|     "s2.add_grade(\"Programming Python\", \"A\")\n", | ||||
|     "s2.add_grade(\"Discrete Math\", \"B+\")" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 11, | ||||
|    "id": "f9deab26", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "Student(name=Adam, id=1, gpa=0)\n", | ||||
|       "Student(name=Beth, id=2, gpa=3.65)\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "print(s1)\n", | ||||
|     "print(s2)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "6aa3358d", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "Perhaps we want to add `Alumni` to our application.\n", | ||||
|     "\n", | ||||
|     "An alum will have some things in common with students:\n", | ||||
|     "\n", | ||||
|     "- They still have a name.\n", | ||||
|     "- We want to remember their major.\n", | ||||
|     "- We'll still want to keep track of their grades/GPA.\n", | ||||
|     "\n", | ||||
|     "We now also:\n", | ||||
|     "\n", | ||||
|     "- Want to record their year of graduation.\n", | ||||
|     "- No longer want to allow grades to be recorded.\n", | ||||
|     "- Want to be able to calculate how long ago they graduated.\n", | ||||
|     "- When displaying them, we want to display their graduation year.\n", | ||||
|     "\n", | ||||
|     "**How to implement?**\n", | ||||
|     "\n", | ||||
|     "We *could* copy `student.py` and rename to `alum.py` and rename the class as needed.\n", | ||||
|     "\n", | ||||
|     "**But copying & pasting is generally a bad idea!**\n", | ||||
|     "\n", | ||||
|     "We'd need to fix bugs & add features in both classes separately.\n", | ||||
|     "\n", | ||||
|     "A new feature in `Student` would need to be copied over to `Alum`, this will quickly get messy.\n" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "d0d61727", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "### Implementation\n", | ||||
|     "\n", | ||||
|     "Instead we will use **inheritance**, which allows us to create a new class from an existing one.  The new class inherits the attributes and methods from the parent.\n", | ||||
|     "\n", | ||||
|     "- **superclass**, **parent**, or **base** class: The pre-existing class.\n", | ||||
|     "- **subclass**, **child**, or **derived** class: The new class that inherits the code (attributes & methods) of another class.\n", | ||||
|     "\n", | ||||
|     "Subclasses can extend/modify the functionality of superclasses.\n", | ||||
|     "\n", | ||||
|     "Syntax:\n", | ||||
|     "\n", | ||||
|     "```python\n", | ||||
|     "class Subclass(Superclass):\n", | ||||
|     "    pass\n", | ||||
|     "```\n", | ||||
|     "\n", | ||||
|     "For example:\n", | ||||
|     "\n", | ||||
|     "```python\n", | ||||
|     "class Alum(Student):\n", | ||||
|     "    pass\n", | ||||
|     "```\n", | ||||
|     "\n", | ||||
|     "At this point, `Alum` is a new class with the exact same implementation as `Student`.\n", | ||||
|     "\n", | ||||
|     "Typically we'll want to add new instance & class variables, methods, etc.\n", | ||||
|     "\n", | ||||
|     "Newly defined features will only apply to instances of `Alum`\n", | ||||
|     "\n", | ||||
|     "It is possible to override parent class behavior, or rely on parent behavior, whichever is needed." | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "e4d45c9f", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "### Adding & Overriding Behavior" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 12, | ||||
|    "id": "47f7dfc9", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "class Alum(Student):\n", | ||||
|     "    def __init__(self, name, grad_year):\n", | ||||
|     "        # call Student's constructor, which contains id logic\n", | ||||
|     "        super().__init__(name)\n", | ||||
|     "        self.graduation_year = grad_year\n", | ||||
|     "        \n", | ||||
|     "    # new behavior\n", | ||||
|     "    def years_since_graduation(self, now):\n", | ||||
|     "        return now - self.graduation_year\n", | ||||
|     "    \n", | ||||
|     "    # overrides parent's add_grade\n", | ||||
|     "    def add_grade(self, course_name, grade):\n", | ||||
|     "        print(\"Sorry, you cannot add grades to Alums\")\n", | ||||
|     "        # we choose not call super().add_grade here\n", | ||||
|     "    \n", | ||||
|     "    # overrides parent's __repr__\n", | ||||
|     "    def __repr__(self):\n", | ||||
|     "        #return f\"Alum(name={self.name}, id={self.id}, gpa={self.gpa}, graduated={self.graduation_year})\"\n", | ||||
|     "        string = super().__repr__()\n", | ||||
|     "        string += \" is an alum\"\n", | ||||
|     "        return string" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 13, | ||||
|    "id": "4811b0fd", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "Student(name=Charlie, id=3, gpa=0) is an alum\n", | ||||
|       "6 years since graduation\n", | ||||
|       "Sorry, you cannot add grades to Alums\n" | ||||
|      ] | ||||
|     }, | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "0" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 13, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "alum1 = Alum(\"Charlie\", 2016)\n", | ||||
|     "print(alum1)\n", | ||||
|     "print(alum1.years_since_graduation(2022), \"years since graduation\")\n", | ||||
|     "alum1.add_grade(\"Python\", \"B\")\n", | ||||
|     "alum1.gpa" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 16, | ||||
|    "id": "18a75666-9986-4e2b-9d55-ec0e0b9e930d", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "alum2 = Alum(\"Charlie\", 2016)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "b6719a36-b827-4373-87e0-23714438b83f", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "3cd672f1", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "### super()\n", | ||||
|     "\n", | ||||
|     "Allows direct access to parent class(es).\n", | ||||
|     "\n", | ||||
|     "Many different ways to be called, but for our purposes we will stick to `super().method_name()` to access parent implementation of `method_name()`" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "f6d2c035", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "### issubclass & isinstance" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 13, | ||||
|    "id": "a46d5b02", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "True" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 13, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "isinstance(7, int)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "d3596a45-2c00-4541-910d-08251d3f9c5f", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# same as?\n", | ||||
|     "type(7) == int" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 6, | ||||
|    "id": "eaa8b6db-e7df-4137-b4c8-67dfa0be06d5", | ||||
|    "metadata": {}, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "False" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 6, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "type(7) == object" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 7, | ||||
|    "id": "0e50f7bc-541e-4fec-9cd6-ad708f312831", | ||||
|    "metadata": {}, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "True" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 7, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "# isinstance checks the inheritance hierarchy \n", | ||||
|     "isinstance(7, object)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "cd55fd8d", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "isinstance([1, 2, 3], list)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "feb9f2ac", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "s1 = Student(\"Sarah\")\n", | ||||
|     "isinstance(s1, Student)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "e6a2f3b1", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# child classes are instances of parent types\n", | ||||
|     "alum1 = Alum(\"Charlie\", 2016)\n", | ||||
|     "isinstance(alum1, Student)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "8f759efb", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# but not vice-versa\n", | ||||
|     "isinstance(s1, Alum)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 15, | ||||
|    "id": "1d52b927", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "ename": "TypeError", | ||||
|      "evalue": "issubclass() arg 1 must be a class", | ||||
|      "output_type": "error", | ||||
|      "traceback": [ | ||||
|       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | ||||
|       "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)", | ||||
|       "Cell \u001b[0;32mIn[15], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;66;03m# takes class names\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;43missubclass\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43malum1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mStudent\u001b[49m\u001b[43m)\u001b[49m\n", | ||||
|       "\u001b[0;31mTypeError\u001b[0m: issubclass() arg 1 must be a class" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "# takes class names\n", | ||||
|     "issubclass(alum1, Student)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 16, | ||||
|    "id": "a71ee1cb", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "False" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 16, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "issubclass(Student, Alum)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "32e11764", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "### `object`\n", | ||||
|     "\n", | ||||
|     "Every object derives from a base class named `object`.\n", | ||||
|     "\n", | ||||
|     "```python\n", | ||||
|     "class Point:\n", | ||||
|     "    def __init__(self, x, y):\n", | ||||
|     "        self.x = y\n", | ||||
|     "\n", | ||||
|     "# Same as: \n", | ||||
|     "\n", | ||||
|     "class Point(object):\n", | ||||
|     "    def __init__(self, x, y):\n", | ||||
|     "        self.x = y\n", | ||||
|     "        self.y = y\n", | ||||
|     "```\n", | ||||
|     "\n", | ||||
|     "### MRO\n", | ||||
|     "\n", | ||||
|     "When we call a function, Python walks up the chain of parent classes to determine the first one that has the method defined.\n", | ||||
|     "\n", | ||||
|     "This is called the **method resolution order**.\n" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 18, | ||||
|    "id": "1d4e00ca", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "Help on Alum in module __main__ object:\n", | ||||
|       "\n", | ||||
|       "class Alum(Student)\n", | ||||
|       " |  Alum(name, grad_year)\n", | ||||
|       " |  \n", | ||||
|       " |  Method resolution order:\n", | ||||
|       " |      Alum\n", | ||||
|       " |      Student\n", | ||||
|       " |      builtins.object\n", | ||||
|       " |  \n", | ||||
|       " |  Methods defined here:\n", | ||||
|       " |  \n", | ||||
|       " |  __init__(self, name, grad_year)\n", | ||||
|       " |      Initialize self.  See help(type(self)) for accurate signature.\n", | ||||
|       " |  \n", | ||||
|       " |  __repr__(self)\n", | ||||
|       " |      Return repr(self).\n", | ||||
|       " |  \n", | ||||
|       " |  add_grade(self, course_name, grade)\n", | ||||
|       " |      # overrides parent's add_grade\n", | ||||
|       " |  \n", | ||||
|       " |  years_since_graduation(self, now)\n", | ||||
|       " |      # new behavior\n", | ||||
|       " |  \n", | ||||
|       " |  ----------------------------------------------------------------------\n", | ||||
|       " |  Readonly properties inherited from Student:\n", | ||||
|       " |  \n", | ||||
|       " |  gpa\n", | ||||
|       " |  \n", | ||||
|       " |  ----------------------------------------------------------------------\n", | ||||
|       " |  Data descriptors inherited from Student:\n", | ||||
|       " |  \n", | ||||
|       " |  __dict__\n", | ||||
|       " |      dictionary for instance variables (if defined)\n", | ||||
|       " |  \n", | ||||
|       " |  __weakref__\n", | ||||
|       " |      list of weak references to the object (if defined)\n", | ||||
|       " |  \n", | ||||
|       " |  ----------------------------------------------------------------------\n", | ||||
|       " |  Data and other attributes inherited from Student:\n", | ||||
|       " |  \n", | ||||
|       " |  next_id_counter = 5\n", | ||||
|       "\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "help(alum1)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "d2519b22", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "## Abstract Base Classes\n", | ||||
|     "\n", | ||||
|     "Sometimes we want to define a class that can't be instantiated directly, but is intended to be inherited from.\n", | ||||
|     "\n", | ||||
|     "These are known as **abstract classes**.  This helps us define an interface, which contains a collection of methods that the **concrete class** must implement.\n", | ||||
|     "\n" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 29, | ||||
|    "id": "9f705ba6", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "def print_dot_prod(v1, v2):\n", | ||||
|     "    \"\"\" prints dot product between two vectors \"\"\"\n", | ||||
|     "    print(v1.dot_product(v2))" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "ec8087af", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "If we want this  method to be polymorphic for vectors of multiple dimensions, such as:" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 38, | ||||
|    "id": "4b20d7b3", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "class Vec2:\n", | ||||
|     "    def __init__(self,x,y):\n", | ||||
|     "        self.x = x\n", | ||||
|     "        self.y = y  \n", | ||||
|     "        \n", | ||||
|     "class Vec3:\n", | ||||
|     "    def __init__(self,x,y,z):\n", | ||||
|     "        self.x = x\n", | ||||
|     "        self.y = y  \n", | ||||
|     "        self.z = z " | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "86d216a7", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "We can force that these types implement an interface (i.e., an abstract base class) such that we can guarantee that objects we pass to ``print_dot_prod`` will always work by forcing them to implement a ``dot_product`` method. \n", | ||||
|     "\n", | ||||
|     "We will define an abstract class called ``Vector`` that has only the required method: \n", | ||||
|     "\n", | ||||
|     "`` def dot_product(self, other) `` " | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 43, | ||||
|    "id": "a2cddb6d", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "from abc import ABC, abstractmethod\n", | ||||
|     "\n", | ||||
|     "class Vector(ABC):    \n", | ||||
|     "    @abstractmethod\n", | ||||
|     "    def dot_product(self, other):\n", | ||||
|     "        pass" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 44, | ||||
|    "id": "a32d26a8", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "ename": "TypeError", | ||||
|      "evalue": "Can't instantiate abstract class Vector with abstract method dot_product", | ||||
|      "output_type": "error", | ||||
|      "traceback": [ | ||||
|       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | ||||
|       "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)", | ||||
|       "Cell \u001b[0;32mIn[44], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;66;03m# we can't instantiate abstract classes\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[43mVector\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", | ||||
|       "\u001b[0;31mTypeError\u001b[0m: Can't instantiate abstract class Vector with abstract method dot_product" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "# we can't instantiate abstract classes\n", | ||||
|     "v = Vector()" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 45, | ||||
|    "id": "5a9ae047", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "class Vec2(Vector):\n", | ||||
|     "    def __init__(self, x, y):\n", | ||||
|     "        self.x = x\n", | ||||
|     "        self.y = y  \n", | ||||
|     "        \n", | ||||
|     "    def dot_product(self, other): \n", | ||||
|     "        return self.x * other.x + self.y * other.y\n", | ||||
|     "        \n", | ||||
|     "class Vec3(Vector):\n", | ||||
|     "    def __init__(self, x, y, z):\n", | ||||
|     "        self.x = x\n", | ||||
|     "        self.y = y  \n", | ||||
|     "        self.z = z \n", | ||||
|     "        \n", | ||||
|     "    def dot_product(self, other): \n", | ||||
|     "        return self.x * other.x + self.y * other.y + self.z * other.z" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 46, | ||||
|    "id": "e25536d1", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "True\n", | ||||
|       "True\n", | ||||
|       "----\n", | ||||
|       "True\n", | ||||
|       "True\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "# now print_dot_prod works\n", | ||||
|     "\n", | ||||
|     "# Vec2 and Vec3 objects are instances of Vector since their classes \n", | ||||
|     "# inherit from the Vector ABC.\n", | ||||
|     "v2a = Vec2(1,2)\n", | ||||
|     "v2b = Vec2(3,4)\n", | ||||
|     "v3a = Vec3(6,7,3)\n", | ||||
|     "v3b = Vec3(1,2,3)\n", | ||||
|     "\n", | ||||
|     "print(isinstance(v2a, Vec2)) \n", | ||||
|     "print(isinstance(v2a, Vector)) \n", | ||||
|     "print(\"----\")\n", | ||||
|     "print(isinstance(v3a, Vec3)) \n", | ||||
|     "print(isinstance(v3a, Vector))" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 47, | ||||
|    "id": "96d41011", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "11\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "print_dot_prod(v2a, v2b)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 48, | ||||
|    "id": "7cf62b14", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "29\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "print_dot_prod(v3a, v3b)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "ebbd1b47-601b-4a65-b493-213ccbedf373", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "## Dataclasses\n", | ||||
|     "\n", | ||||
|     "Python 3.7 added `dataclasses` as a handy way to create classes that are mostly responsible for representing data. These classes often have few or no methods defined." | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 53, | ||||
|    "id": "4f98cf62-52da-4072-bf5a-da7de3ad9a98", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "from dataclasses import dataclass\n", | ||||
|     "\n", | ||||
|     "@dataclass\n", | ||||
|     "class InventoryItem:\n", | ||||
|     "    \"\"\"Class for keeping track of an item in inventory.\"\"\"\n", | ||||
|     "    name: str\n", | ||||
|     "    unit_price: float\n", | ||||
|     "    quantity_on_hand: int = 0\n", | ||||
|     "\n", | ||||
|     "    def total_cost(self) -> float:\n", | ||||
|     "        return self.unit_price * self.quantity_on_hand\n", | ||||
|     "#InventoryItem = dataclass(InventoryItem)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "af1f9445-29c7-4fd8-8648-596fa3a12006", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 54, | ||||
|    "id": "8faf9c31-33e4-42f1-a8e8-8b72a7f1f96f", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "wrench = InventoryItem(\"Wrench\", 12.95, 10)\n", | ||||
|     "hammer = InventoryItem(\"Hammer\", 16, 8)\n", | ||||
|     "nails = InventoryItem(\"Nails\", 0.03, 1000)\n", | ||||
|     "saw = InventoryItem(\"Saw\", 99)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "4a088329-2f4d-4548-a750-b0f02df5dc9b", | ||||
|    "metadata": {}, | ||||
|    "source": [ | ||||
|     "Dataclasses get an automatic `__init__`, `__repr__`, `__eq__`, and several other helpful options.  (Even more is possible via the decorator: https://docs.python.org/3/library/dataclasses.html)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 55, | ||||
|    "id": "e3a8dd5f-55bc-49d2-af83-79c977393b0d", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "data": { | ||||
|       "text/plain": [ | ||||
|        "30.0" | ||||
|       ] | ||||
|      }, | ||||
|      "execution_count": 55, | ||||
|      "metadata": {}, | ||||
|      "output_type": "execute_result" | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "nails.total_cost()" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "markdown", | ||||
|    "id": "d8f9a214-3277-40a9-b50c-2aabf5794079", | ||||
|    "metadata": { | ||||
|     "tags": [] | ||||
|    }, | ||||
|    "source": [ | ||||
|     "Beyond this, additional methods/staticmethods/etc. can be defined in the usual way.\n", | ||||
|     "\n", | ||||
|     "This syntax uses Python's type-hinting, and if you're looking to use it you'll want to get familiar with the rules\n", | ||||
|     "around complex types: https://docs.python.org/3/library/typing.html\n", | ||||
|     "\n", | ||||
|     "**Note: This syntax has been evolving rapidly from Python 3.6->now.  This is one are where making sure you have a current (>=3.10) version of Python will matter.**" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 57, | ||||
|    "id": "3c1a8baa-3840-437d-b6ad-d880a4676423", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# instead of returning tuples and \n", | ||||
|     "# remembering the positional order, can instead\n", | ||||
|     "@dataclass\n", | ||||
|     "class RetType:\n", | ||||
|     "    data: list[int]\n", | ||||
|     "    counter: int\n", | ||||
|     "\n", | ||||
|     "def fn():\n", | ||||
|     "    return RetType([], counter)\n" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "7097097d-b7a6-437a-8f60-f12d38df6a1b", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": null, | ||||
|    "id": "73195ac4-c0cc-4f8b-8852-2b63168e1713", | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [] | ||||
|   } | ||||
|  ], | ||||
|  "metadata": { | ||||
|   "kernelspec": { | ||||
|    "display_name": "Python 3 (ipykernel)", | ||||
|    "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.10.15" | ||||
|   }, | ||||
|   "toc": { | ||||
|    "base_numbering": 1, | ||||
|    "nav_menu": {}, | ||||
|    "number_sections": false, | ||||
|    "sideBar": true, | ||||
|    "skip_h1_title": false, | ||||
|    "title_cell": "Table of Contents", | ||||
|    "title_sidebar": "Contents", | ||||
|    "toc_cell": false, | ||||
|    "toc_position": { | ||||
|     "height": "calc(100% - 180px)", | ||||
|     "left": "10px", | ||||
|     "top": "150px", | ||||
|     "width": "256.933px" | ||||
|    }, | ||||
|    "toc_section_display": true, | ||||
|    "toc_window_display": false | ||||
|   } | ||||
|  }, | ||||
|  "nbformat": 4, | ||||
|  "nbformat_minor": 5 | ||||
| } | ||||
		Loading…
	
		Reference in a new issue