{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Inheritance I\n", "\n", "- [Download the lecture notes](https://philchodrow.github.io/PIC16A/content/object_oriented_programming/inheritance_I.ipynb). \n", "\n", "Often, we face a problem that is *almost* solved by an existing class. For example, suppose I want to use Python to keep track of my grocery shopping. I can use a `dict` to log the items in my pantry: " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "pantry = {\n", " \"rice (lbs)\" : 2,\n", " \"harissa (jars)\" : 1,\n", " \"onions\" : 5,\n", " \"lemons\" : 3\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now suppose I go shopping, and I come back with: " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "shopping_trip = {\n", " \"rice (lbs)\" : 1,\n", " \"onions\" : 2,\n", " \"spinach (lbs)\" : 1\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What I'd like to do is add these `dict`s together in the obvious way, obtaining the `dict`\n", "\n", "```\n", "{\n", " \"rice (lbs)\" : 3,\n", " \"harissa (jars)\" : 1,\n", " \"onions\" : 7,\n", " \"lemons\" : 3,\n", " \"spinach (lbs)\" : 1\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unfortunately, the native implementation of `dict`s doesn't support this kind of operation. For our first example, we will implement a new class that **inherits** from `dict`, and which supports basic arithmetic. In particular, once we're done, the following will achieve the expected result: \n", "\n", "```\n", "pantry += shopping_trip\n", "```\n", "\n", "To write a class `classA` that inherits from `classB`, just declare `class classA(classB)`. For example: " ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "class ArithmeticDict(dict):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just by including the inheritance, this very boring class already does everything that a `dict` can do. In fact, it IS a `dict` -- that is, it is an *instance* of the `dict` class. " ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "({'a': 1, 'b': 2}, __main__.ArithmeticDict, True)" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = ArithmeticDict({'a' : 1, 'b' : 2})\n", "x, type(x), isinstance(x, dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do normal `dict` methods: " ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "({'a': 1, 'b': 2, 'c': 3}, 1)" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.update({'c' : 3})\n", "x, x['a']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Pause for a moment:** why were we able to do: \n", "\n", "```a = ArithmeticDict({'a' : 1, 'b' : 2})```\n", "\n", "and get the expected result? " ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [], "source": [ "# behind the scenes\n", "# which __init__() method is this? \n", "\n", "a = ArithmeticDict.__init__({'a' : 1, 'b' : 2}) " ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [], "source": [ "b = dict.__init__({'a' : 1, 'b' : 2})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, this doesn't give us anything new yet. The important part is that we are now able to define new methods that will be available only for the `ArithmeticDict` class. " ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [], "source": [ "class ArithmeticDict(dict):\n", " \"\"\"\n", " A dictionary class that supports entrywise addition\n", " \"\"\"\n", " \n", " def __add__(self, to_add):\n", " \"\"\"\n", " Add two ArithmeticDicts entrywise. \n", " \"\"\"\n", " new = {}\n", " keys1 = set(self.keys())\n", " keys2 = set(to_add.keys())\n", " all_keys = keys1.union(keys2)\n", "\n", " for key in all_keys:\n", " new.update({key : self.get(key,0) + to_add.get(key,0)})\n", " \n", " return ArithmeticDict(new)" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [], "source": [ "x = ArithmeticDict({'a' : 1, 'b' : 2})\n", "y = ArithmeticDict({'a' : 1, 'b' : 3, 'c' : 7})" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'a': 2, 'b': 5, 'c': 7}" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x+y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'm now able to update my pantry: " ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [], "source": [ "pantry = {\n", " \"rice (lbs)\" : 2,\n", " \"harissa (jars)\" : 1,\n", " \"onions\" : 5,\n", " \"lemons\" : 3\n", "}\n", "\n", "shopping_trip = {\n", " \"rice (lbs)\" : 1,\n", " \"onions\" : 2,\n", " \"spinach (lbs)\" : 1\n", "}" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'rice (lbs)': 2, 'harissa (jars)': 1, 'onions': 5, 'lemons': 3}" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pantry = ArithmeticDict(pantry)\n", "pantry" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'rice (lbs)': 1, 'onions': 2, 'spinach (lbs)': 1}" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "shopping_trip = ArithmeticDict(shopping_trip)\n", "shopping_trip" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'spinach (lbs)': 1,\n", " 'lemons': 3,\n", " 'onions': 7,\n", " 'rice (lbs)': 3,\n", " 'harissa (jars)': 1}" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pantry += shopping_trip \n", "# OR pantry = pantry + shopping_trip\n", "pantry" ] } ], "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.8.3" } }, "nbformat": 4, "nbformat_minor": 4 }