-
-
Save eds-satish/9b3e2e7ff4ad6afc54845e7bfc31cab1 to your computer and use it in GitHub Desktop.
Objected-oriented programming with Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "# Object-oriented programming with Python\n", | |
| "\n", | |
| "> After all, we're all consenting adults here - GVR\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### What?\n", | |
| "\n", | |
| "- Evaluate Python's OOP implementation\n", | |
| "- Contrast with traditional OOP languages\n", | |
| "- Assess support for pure OOP implementation\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### What not?\n", | |
| "\n", | |
| "- Primer to OOP\n", | |
| "- Primer to Python" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### Why?\n", | |
| "\n", | |
| "- Modularity\n", | |
| "- Maintainability\n", | |
| "- Extensibility" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### Why not?\n", | |
| "\n", | |
| "- state vs functional programming\n", | |
| "- inheritance vs composition\n", | |
| "- monkey banana problem " | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### How?\n", | |
| "\n", | |
| "- Inheritance\n", | |
| "- Encapuslation\n", | |
| "- Polymorphism" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### class keyword\n", | |
| "- User defined objects; blueprint" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " pass\n", | |
| "\n", | |
| "corona = Apocalypse()\n", | |
| "print(type(corona))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### Attributes\n", | |
| "- class vs instance variables" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " def __init__(self, name: str):\n", | |
| " self.name = name\n", | |
| "\n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing {self.name} to you this fall!\")\n", | |
| "\n", | |
| "corona = Apocalypse(\"corona\")\n", | |
| "corona.print_message()\n", | |
| "\n", | |
| "k_pop = Apocalypse(\"k_pop\")\n", | |
| "k_pop.print_message()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " name: str = \"Apocalypse\"\n", | |
| " \n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing {Apocalypse.name} to you this fall!\")\n", | |
| "\n", | |
| "corona = Apocalypse()\n", | |
| "k_pop = Apocalypse()\n", | |
| "\n", | |
| "corona.print_message()\n", | |
| "\n", | |
| "Apocalypse.name = \"2012\"\n", | |
| "\n", | |
| "corona.print_message()\n", | |
| "k_pop.print_message()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Inheritance\n", | |
| "- Projection from a basis\n", | |
| "- Multiple inheritance" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Epidemic(Apocalypse):\n", | |
| " def __init__(self, name: str, epidemic_type: str):\n", | |
| " super().__init__(name)\n", | |
| " self.epidemic_type = epidemic_type\n", | |
| "\n", | |
| "corona = Epidemic(\"Corona\", \"Virus\")\n", | |
| "corona.print_message()\n", | |
| "print(corona.epidemic_type)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " def __init__(self, name: str):\n", | |
| " self.name = name\n", | |
| "\n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing {self.name} to you this fall!\")\n", | |
| "\n", | |
| "class Controversy:\n", | |
| " def __init__(self, origin: str):\n", | |
| " self.origin = origin\n", | |
| "\n", | |
| " def print_source(self):\n", | |
| " print(f\"Brought to you by {self.origin}!\")\n", | |
| "\n", | |
| "class Epidemic(Apocalypse, Controversy):\n", | |
| " def __init__(self, name: str, epidemic_type: str, origin: str):\n", | |
| " Apocalypse.__init__(self, name)\n", | |
| " Controversy.__init__(self, origin)\n", | |
| " self.epidemic_type = epidemic_type\n", | |
| "\n", | |
| "\n", | |
| "corona = Epidemic(\"Corona\", \"Virus\", \"Trump\")\n", | |
| "corona.print_message()\n", | |
| "corona.print_source()\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>\n", | |
| "\n", | |
| "### Enapsulation\n", | |
| "- Separation of concerns\n", | |
| "- Information hiding\n", | |
| "- Python's `_private` vs `__scrambled`\n", | |
| "- No enforcement of encapsulation" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " def __init__(self, name: str):\n", | |
| " self.name = name\n", | |
| " self._priority = 100\n", | |
| "\n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing {self.name} to you this fall!\")\n", | |
| "\n", | |
| "class Epidemic(Apocalypse):\n", | |
| " def __init__(self, name: str, epidemic_type: str):\n", | |
| " super().__init__(name)\n", | |
| " self.epidemic_type = epidemic_type\n", | |
| "\n", | |
| "\n", | |
| "corona = Epidemic(\"Corona\", \"Virus\")\n", | |
| "corona.print_message()\n", | |
| "\n", | |
| "print(corona._priority)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>\n", | |
| "\n", | |
| "### Polymorphism\n", | |
| "- runtime vs compile time\n", | |
| "- overloading vs overidding\n", | |
| "- duck typing" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 174, | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Bringing epidemic Corona/Virus to you this fall!\n", | |
| "Bringing epidemic Corona/Virus to you this fall!\n", | |
| "False\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "class Apocalypse:\n", | |
| " def __init__(self, name: str):\n", | |
| " self.name = name\n", | |
| "\n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing {self.name} to you this fall!\")\n", | |
| " \n", | |
| " def __eq__(self, other) -> bool:\n", | |
| " return self.name == other.name\n", | |
| "\n", | |
| "\n", | |
| "class Epidemic(Apocalypse):\n", | |
| " def __init__(self, name: str, epidemic_type: str):\n", | |
| " super().__init__(name)\n", | |
| " self.epidemic_type = epidemic_type\n", | |
| " \n", | |
| " def print_message(self):\n", | |
| " print(f\"Bringing epidemic {self.name}/{self.epidemic_type} to you this fall!\")\n", | |
| " \n", | |
| " def mutate(self, force=False):\n", | |
| " if force:\n", | |
| " self.name += \"_mutated\"\n", | |
| " return self\n", | |
| "\n", | |
| " \n", | |
| "def print_message(source: Apocalypse):\n", | |
| " source.print_message()\n", | |
| "\n", | |
| "corona = Epidemic(\"Corona\", \"Virus\")\n", | |
| "\n", | |
| "print_message(corona)\n", | |
| "\n", | |
| "corona.mutate(False)\n", | |
| "print_message(corona)\n", | |
| "\n", | |
| "ebola = Epidemic(\"Ebola\", \"Virus\")\n", | |
| "print(corona == ebola)\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>\n", | |
| "\n", | |
| "## With a little help from my friends\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "\n", | |
| "from abc import ABC\n", | |
| "from abc import abstractmethod\n", | |
| "\n", | |
| "from typing import Generic\n", | |
| "from typing import TypeVar\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 160, | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Bringing epidemic Corona_mutated/Virus to you this fall!\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "OPERAND_TYPE = TypeVar(\"TYPE\")\n", | |
| "\n", | |
| "\n", | |
| "class DoomsdayDevice(ABC, Generic[OPERAND_TYPE]):\n", | |
| " \n", | |
| " @abstractmethod\n", | |
| " def destroy(self, source: OPERAND_TYPE) -> OPERAND_TYPE:\n", | |
| " ...\n", | |
| "\n", | |
| "class WuhanLab(DoomsdayDevice[Epidemic]):\n", | |
| " \n", | |
| " def destroy(self, source: Epidemic) -> Epidemic:\n", | |
| " return source.mutate(True)\n", | |
| "\n", | |
| "corona = Epidemic(\"Corona\", \"Virus\")\n", | |
| "wuhan_lab = WuhanLab()\n", | |
| "wuhan_lab.destroy(corona).print_message()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 161, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "OPERAND_TYPE = TypeVar(\"TYPE\")\n", | |
| "\n", | |
| "\n", | |
| "class DoomsdayDevice(ABC, Generic[OPERAND_TYPE]):\n", | |
| " \n", | |
| " @abstractmethod\n", | |
| " def destroy(self) -> OPERAND_TYPE:\n", | |
| " ...\n", | |
| " \n", | |
| " @property\n", | |
| " def origin(self) -> OPERAND_TYPE:\n", | |
| " return self.origin\n", | |
| " \n", | |
| " @origin.setter\n", | |
| " def origin(self, data_source):\n", | |
| " self.origin = data_source\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "<br>\n", | |
| "\n", | |
| "### Attrs vs Dataclasses\n", | |
| "- Both reduce boilerplate\n", | |
| "- mature interfaces vs stdlib\n", | |
| "- Attr with validations, converters and slotting" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 179, | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "False\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "import attr\n", | |
| "\n", | |
| "@attr.s(auto_attribs=True)\n", | |
| "class Apocalypse:\n", | |
| " name: str = \"Apocalypse\"\n", | |
| "\n", | |
| "corona = Apocalypse(\"corona\")\n", | |
| "corona_02 = Apocalypse(\"corona\")\n", | |
| "\n", | |
| "print(corona == corona_02)\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 190, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "def is_not_empty(instance, attribute, value):\n", | |
| " if not value:\n", | |
| " raise ValueError(f\"{attribute.name} cannot be empty\")\n", | |
| "\n", | |
| "def non_empty_string():\n", | |
| " return attr.validators.and_(attr.validators.instance_of(str), is_not_empty)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 198, | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Apocalypse(name='corona', countries=[])\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "from typing import List\n", | |
| "\n", | |
| "@attr.s(frozen=True)\n", | |
| "class Apocalypse:\n", | |
| " name: str = attr.ib(default=\"Apocalypse\", validator=non_empty_string())\n", | |
| " countries: List[str] = attr.ib(factory=list, init=False)\n", | |
| "\n", | |
| "corona = Apocalypse(\"corona\")\n", | |
| "print(corona)\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Fun reads\n", | |
| "\n", | |
| "1. [Python and encapsulation](https://mail.python.org/pipermail/tutor/2003-October/025932.html)\n", | |
| "2. [Monkey and the banana](https://medium.com/@Sharpninja/banana-monkey-jungle-problem-a0f4ed46e9aa)\n", | |
| "3. [Borg pattern](http://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/)\n", | |
| "4. [Class variables performance](https://stackoverflow.com/a/2715003/12715289)\n", | |
| "5. [Name mangling](https://stackoverflow.com/questions/7456807/python-name-mangling)\n", | |
| "6. [What is OOP; here lie dragons](http://wiki.c2.com/?DefinitionsForOo)\n", | |
| "7. [Single Dispatch PEP](https://www.python.org/dev/peps/pep-0443/)\n", | |
| "8. [Violations of LSP](https://github.com/python/typing/issues/487)\n", | |
| "9. [Magic methods](https://rszalski.github.io/magicmethods/)\n", | |
| "10. [Attrs vs Dataclasses](https://www.revsys.com/tidbits/dataclasses-and-attrs-when-and-why/)" | |
| ] | |
| } | |
| ], | |
| "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.3" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 4 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment