{ "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__, )\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 }