51042-notes/12.inheritance.ipynb
2024-11-08 22:22:09 -06:00

1180 lines
29 KiB
Plaintext

{
"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": 1,
"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",
" self.extracurriculars = []\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": 45,
"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": 46,
"id": "f9deab26",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Student(name=Adam, id=8, gpa=0)\n",
"Student(name=Beth, id=9, 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": 49,
"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",
" raise NotImplementedError(\"cannot add grades to Alum\")\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": 51,
"id": "4811b0fd",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Student(name=Charlie, id=13, gpa=0) is an alum\n",
"6 years since graduation\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 51,
"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": 54,
"id": "18a75666-9986-4e2b-9d55-ec0e0b9e930d",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"alum2 = Alum(\"Charlie\", 2016)"
]
},
{
"cell_type": "code",
"execution_count": 56,
"id": "b6719a36-b827-4373-87e0-23714438b83f",
"metadata": {
"tags": []
},
"outputs": [
{
"ename": "TypeError",
"evalue": "unsupported operand type(s) for +: 'Alum' and 'Alum'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[56], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43malum1\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43malum2\u001b[49m\n",
"\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'Alum' and 'Alum'"
]
}
],
"source": [
"alum1 + alum2"
]
},
{
"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": 8,
"id": "a46d5b02",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(7, int)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "d3596a45-2c00-4541-910d-08251d3f9c5f",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# same as?\n",
"type(7) == int"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "eaa8b6db-e7df-4137-b4c8-67dfa0be06d5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(7) == object"
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "9cfc8480-ecca-4f80-9dfe-70a655bc5b3c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(7, object)"
]
},
{
"cell_type": "code",
"execution_count": 62,
"id": "0e50f7bc-541e-4fec-9cd6-ad708f312831",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# isinstance checks the inheritance hierarchy \n",
"isinstance(alum2, object)"
]
},
{
"cell_type": "code",
"execution_count": 64,
"id": "34e491d5-ac5c-4dc9-b059-491e0f0f73b0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(alum2) == Student"
]
},
{
"cell_type": "code",
"execution_count": 65,
"id": "cd55fd8d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance([1, 2, 3], list)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "feb9f2ac",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s1 = Student(\"Sarah\")\n",
"isinstance(s1, Student)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "e6a2f3b1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# child classes are instances of parent types\n",
"alum1 = Alum(\"Charlie\", 2016)\n",
"isinstance(alum1, Student)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "8f759efb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# but not vice-versa\n",
"isinstance(s1, Alum)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"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[20], 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": 22,
"id": "a71ee1cb",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(Alum, Student)"
]
},
{
"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": 66,
"id": "1d4e00ca",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Help on class Alum in module __main__:\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 = 16\n",
"\n"
]
}
],
"source": [
"help(Alum)"
]
},
{
"cell_type": "code",
"execution_count": 67,
"id": "45f732f0-2036-416e-a44c-70b99b18a960",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(__main__.Alum, __main__.Student, object)"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Alum.__mro__"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "592f6930-cf36-4ec1-a4f3-ea1947fa7173",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Help on class super in module builtins:\n",
"\n",
"class super(object)\n",
" | super() -> same as super(__class__, <first argument>)\n",
" | super(type) -> unbound super object\n",
" | super(type, obj) -> bound super object; requires isinstance(obj, type)\n",
" | super(type, type2) -> bound super object; requires issubclass(type2, type)\n",
" | Typical use to call a cooperative superclass method:\n",
" | class C(B):\n",
" | def meth(self, arg):\n",
" | super().meth(arg)\n",
" | This works for class methods too:\n",
" | class C(B):\n",
" | @classmethod\n",
" | def cmeth(cls, arg):\n",
" | super().cmeth(arg)\n",
" | \n",
" | Methods defined here:\n",
" | \n",
" | __get__(self, instance, owner=None, /)\n",
" | Return an attribute of instance, which is of type owner.\n",
" | \n",
" | __getattribute__(self, name, /)\n",
" | Return getattr(self, name).\n",
" | \n",
" | __init__(self, /, *args, **kwargs)\n",
" | Initialize self. See help(type(self)) for accurate signature.\n",
" | \n",
" | __repr__(self, /)\n",
" | Return repr(self).\n",
" | \n",
" | ----------------------------------------------------------------------\n",
" | Static methods defined here:\n",
" | \n",
" | __new__(*args, **kwargs) from builtins.type\n",
" | Create and return a new object. See help(type) for accurate signature.\n",
" | \n",
" | ----------------------------------------------------------------------\n",
" | Data descriptors defined here:\n",
" | \n",
" | __self__\n",
" | the instance invoking super(); may be None\n",
" | \n",
" | __self_class__\n",
" | the type of the instance invoking super(); may be None\n",
" | \n",
" | __thisclass__\n",
" | the class invoking super()\n",
"\n"
]
}
],
"source": [
"help(super)"
]
},
{
"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",
" def dot_product(self, other):\n",
" ...\n",
" \n",
"class Vec3:\n",
" def __init__(self,x,y,z):\n",
" self.x = x\n",
" self.y = y \n",
" self.z = z \n",
"\n",
" def dot(self, other):\n",
" ..."
]
},
{
"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": 72,
"id": "a2cddb6d",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from abc import ABC, abstractmethod\n",
"\n",
"class Vector(ABC): \n",
" def print_x(self):\n",
" print(self.x)\n",
" \n",
" @abstractmethod\n",
" def dot_product(self, other):\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 71,
"id": "a32d26a8",
"metadata": {
"tags": []
},
"outputs": [
{
"ename": "TypeError",
"evalue": "Can't instantiate abstract class Vector with abstract methods __repr__, 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[71], 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 methods __repr__, dot_product"
]
}
],
"source": [
"# we can't instantiate abstract classes\n",
"v = Vector()"
]
},
{
"cell_type": "code",
"execution_count": 75,
"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": 76,
"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": 78,
"id": "69ac4241-b251-4485-9ba9-3b0348d82c34",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"v2a.print_x()"
]
},
{
"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": 79,
"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": 43,
"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)\n",
"saw2 = InventoryItem(\"Saw\", 99)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "94f14911-335c-4097-8658-b9a30a70d53b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"saw == saw2"
]
},
{
"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": [
"functools."
]
}
],
"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
}