Skip to content

Instantly share code, notes, and snippets.

@eds-satish
Forked from kanmaytacker/oop_python.ipynb
Created May 19, 2023 04:21
Show Gist options
  • Select an option

  • Save eds-satish/9b3e2e7ff4ad6afc54845e7bfc31cab1 to your computer and use it in GitHub Desktop.

Select an option

Save eds-satish/9b3e2e7ff4ad6afc54845e7bfc31cab1 to your computer and use it in GitHub Desktop.
Objected-oriented programming with Python
Display the source blob
Display the rendered blob
Raw
{
"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