diff options
Diffstat (limited to 'notebooks/python')
| -rw-r--r-- | notebooks/python/python_oo.ipynb | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/notebooks/python/python_oo.ipynb b/notebooks/python/python_oo.ipynb new file mode 100644 index 0000000..0b33a09 --- /dev/null +++ b/notebooks/python/python_oo.ipynb | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | { | ||
| 2 | "cells": [ | ||
| 3 | { | ||
| 4 | "cell_type": "markdown", | ||
| 5 | "id": "2612fc97-1e84-4cc1-bd55-4cc58dbd5074", | ||
| 6 | "metadata": {}, | ||
| 7 | "source": [ | ||
| 8 | "# Introduction to object-oriented programming in Python\n", | ||
| 9 | "\n", | ||
| 10 | "Simeon Simeonov @ Statnett" | ||
| 11 | ] | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "cell_type": "markdown", | ||
| 15 | "id": "84139fa9-733e-4871-a2f9-920f6e7c4911", | ||
| 16 | "metadata": {}, | ||
| 17 | "source": [ | ||
| 18 | "# Goals\n", | ||
| 19 | "\n", | ||
| 20 | "- present the Python programming language in a different way than [https://docs.python.org](https://docs.python.org)\n", | ||
| 21 | "\n", | ||
| 22 | "- avoid information overload\n", | ||
| 23 | "\n", | ||
| 24 | "- use examples and interaction rather than documents and slides\n", | ||
| 25 | "\n", | ||
| 26 | "\n", | ||
| 27 | "## Target audience\n", | ||
| 28 | "\n", | ||
| 29 | "- beginner Python programmers\n", | ||
| 30 | "\n", | ||
| 31 | "- analysts using Python as a tool" | ||
| 32 | ] | ||
| 33 | }, | ||
| 34 | { | ||
| 35 | "cell_type": "markdown", | ||
| 36 | "id": "d6d3d0e7-c1d5-4119-940e-0f94ee7542ec", | ||
| 37 | "metadata": {}, | ||
| 38 | "source": [ | ||
| 39 | "# What is object-oriented programming?\n", | ||
| 40 | "\n", | ||
| 41 | "Object-oriented programming does **not** mean using an object-oriented programming language.\n", | ||
| 42 | "\n", | ||
| 43 | "It is rather a programming paradigm based on the concept of object, as well as on some general principles and best practices aiming at:\n", | ||
| 44 | "\n", | ||
| 45 | "- improving readability\n", | ||
| 46 | "\n", | ||
| 47 | "- improving re-usability\n", | ||
| 48 | "\n", | ||
| 49 | "- improving modularity\n", | ||
| 50 | "\n", | ||
| 51 | "- providing foundation for a more intuitive design" | ||
| 52 | ] | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | "cell_type": "markdown", | ||
| 56 | "id": "ad19da5c-3c21-4130-9225-d8f7cb2a7f40", | ||
| 57 | "metadata": {}, | ||
| 58 | "source": [ | ||
| 59 | "# General principles\n", | ||
| 60 | "\n", | ||
| 61 | "The following four concepts / principles are presented in most object-oriented programming books:\n", | ||
| 62 | "\n", | ||
| 63 | "- separate and hide \"private\" details from the outside world and / or child (inheriting) functionality (*encapsulation*) - supports *separation of concerns*\n", | ||
| 64 | "\n", | ||
| 65 | "- separate the interface from its implementation (*abstraction*)\n", | ||
| 66 | "\n", | ||
| 67 | "- inherit and extend / adapt existing functionality, through \"is-a\" relationship hierarchy (*inheritance*)\n", | ||
| 68 | "\n", | ||
| 69 | "- execute different code / functionality based on the object's place in the hierarchy (polymorphism)" | ||
| 70 | ] | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | "cell_type": "markdown", | ||
| 74 | "id": "22b2bf7e-764b-44c8-a9fd-83733319fc6e", | ||
| 75 | "metadata": {}, | ||
| 76 | "source": [ | ||
| 77 | "# Building blocks and definitions\n", | ||
| 78 | "\n", | ||
| 79 | "**Note:** \"Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms.\" - The Python tutorial [https://docs.python.org](https://docs.python.org)\n", | ||
| 80 | "\n", | ||
| 81 | "When talking about object-oriented programming, the following building blocks are involved:\n", | ||
| 82 | "\n", | ||
| 83 | "- class - a blueprint / template for creating objects\n", | ||
| 84 | "\n", | ||
| 85 | "- object - an instance of a class that may contain its own attributes as well as references to its class' attributes\n", | ||
| 86 | "\n", | ||
| 87 | "- attribute - variable, property, function defined in the class and present in its instances\n", | ||
| 88 | "\n", | ||
| 89 | "- class variable - attribute of which a single copy exists, regardless of how many instances of the class exist\n", | ||
| 90 | "\n", | ||
| 91 | "- object / instance variable - attribute for which each instantiated object of the class has a separate copy, or instance\n", | ||
| 92 | "\n", | ||
| 93 | "- method - member function - function that is an attribute" | ||
| 94 | ] | ||
| 95 | }, | ||
| 96 | { | ||
| 97 | "cell_type": "markdown", | ||
| 98 | "id": "9e15c843-f65b-4bd3-a536-e0217b3947f8", | ||
| 99 | "metadata": {}, | ||
| 100 | "source": [ | ||
| 101 | "# The object-oriented world of Python\n", | ||
| 102 | "\n", | ||
| 103 | "Some additional concepts and definitions are introduced in Python, as well as in some other programming languages:\n", | ||
| 104 | "\n", | ||
| 105 | "- metaclass - a class whose instances are classes\n", | ||
| 106 | "\n", | ||
| 107 | "- property - attribute that provides a flexible mechanism to read, write, or compute the value of a \"private\" attribute\n", | ||
| 108 | "\n", | ||
| 109 | "- type - old C object-oriented term, now (in Python 3) considered to be the same as class\n", | ||
| 110 | "\n", | ||
| 111 | "As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.\n", | ||
| 112 | "\n", | ||
| 113 | "Each object in Python has an ID - an integer which is guaranteed to be unique and constant for this object during its lifetime.\n", | ||
| 114 | "Two objects with non-overlapping lifetimes may have the same *id()* value. In *CPython* this is the address of the object in memory.\n", | ||
| 115 | "\n", | ||
| 116 | "Python has automatic memory management using reference counting. When an object no longer has\n", | ||
| 117 | "any references, the garbage collector kicks inn and removes the object from memory." | ||
| 118 | ] | ||
| 119 | }, | ||
| 120 | { | ||
| 121 | "cell_type": "markdown", | ||
| 122 | "id": "d647b1ad-948d-495d-bb30-15761b806354", | ||
| 123 | "metadata": {}, | ||
| 124 | "source": [ | ||
| 125 | "# Example1\n", | ||
| 126 | "\n", | ||
| 127 | "We will create and extend classes for representing points and vectors in 2D space" | ||
| 128 | ] | ||
| 129 | }, | ||
| 130 | { | ||
| 131 | "cell_type": "code", | ||
| 132 | "execution_count": 7, | ||
| 133 | "id": "391e816e-c53b-454b-9e9f-3587234f1fea", | ||
| 134 | "metadata": {}, | ||
| 135 | "outputs": [ | ||
| 136 | { | ||
| 137 | "name": "stdout", | ||
| 138 | "output_type": "stream", | ||
| 139 | "text": [ | ||
| 140 | "2:8\n", | ||
| 141 | "2:8\n", | ||
| 142 | "2\n", | ||
| 143 | "2\n" | ||
| 144 | ] | ||
| 145 | }, | ||
| 146 | { | ||
| 147 | "ename": "AttributeError", | ||
| 148 | "evalue": "'Point' object has no attribute '__y'", | ||
| 149 | "output_type": "error", | ||
| 150 | "traceback": [ | ||
| 151 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | ||
| 152 | "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", | ||
| 153 | "Cell \u001b[0;32mIn [7], line 69\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[38;5;66;03m# we \"should not\" be accessing private and protected attributes directly\u001b[39;00m\n\u001b[1;32m 67\u001b[0m \u001b[38;5;28mprint\u001b[39m(my_first_point\u001b[38;5;241m.\u001b[39m_x)\n\u001b[0;32m---> 69\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mmy_first_point\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__y\u001b[49m)\n", | ||
| 154 | "\u001b[0;31mAttributeError\u001b[0m: 'Point' object has no attribute '__y'" | ||
| 155 | ] | ||
| 156 | } | ||
| 157 | ], | ||
| 158 | "source": [ | ||
| 159 | "class Point:\n", | ||
| 160 | " \"\"\"Basic class for representing points in 2D space\"\"\"\n", | ||
| 161 | "\n", | ||
| 162 | " def __init__(self, x: int, y: int):\n", | ||
| 163 | " \"\"\"\n", | ||
| 164 | " Called after the instance has been created (by __new__()), but before\n", | ||
| 165 | " it is returned to the caller. The arguments are those passed to the\n", | ||
| 166 | " class constructor expression.\n", | ||
| 167 | " \n", | ||
| 168 | " If a base class has an __init__() method, the derived class’s\n", | ||
| 169 | " __init__() method, if any, must explicitly call it to ensure proper\n", | ||
| 170 | " initialization of the base class part of the instance;\n", | ||
| 171 | " for example: super().__init__([args...]).\n", | ||
| 172 | " \"\"\"\n", | ||
| 173 | " self._x = x # _ indicates \"protected\" attribute\n", | ||
| 174 | " self.__y = y # not a typo: __ indicates \"private\" attribute\n", | ||
| 175 | "\n", | ||
| 176 | " def __repr__(self):\n", | ||
| 177 | " \"\"\"\n", | ||
| 178 | " Called by the repr() built-in function to compute the “official”\n", | ||
| 179 | " string representation of an object. If at all possible, this should\n", | ||
| 180 | " look like a valid Python expression that could be used to recreate an\n", | ||
| 181 | " object with the same value (given an appropriate environment). If this\n", | ||
| 182 | " is not possible, a string of the form <...some useful description...>\n", | ||
| 183 | " should be returned. The return value must be a string object.\n", | ||
| 184 | " \n", | ||
| 185 | " If a class defines __repr__() but not __str__(), then __repr__() is\n", | ||
| 186 | " also used when an “informal” string representation of instances of\n", | ||
| 187 | " that class is required. This is typically used for debugging, so it is\n", | ||
| 188 | " important that the representation is information-rich and unambiguous.\n", | ||
| 189 | " \"\"\"\n", | ||
| 190 | " return f\"<Point(x={self._x!r}, y={self.__y!r})>\"\n", | ||
| 191 | "\n", | ||
| 192 | " def __str__(self):\n", | ||
| 193 | " \"\"\"\n", | ||
| 194 | " Called by str(object) and the built-in functions format() and print()\n", | ||
| 195 | " to compute the “informal” or nicely printable string representation of\n", | ||
| 196 | " an object. The return value must be a string object.\n", | ||
| 197 | "\n", | ||
| 198 | " This method differs from object.__repr__() in that there is no\n", | ||
| 199 | " expectation that __str__() return a valid Python expression: a more\n", | ||
| 200 | " convenient or concise representation can be used.\n", | ||
| 201 | "\n", | ||
| 202 | " The default implementation defined by the built-in type object calls\n", | ||
| 203 | " object.__repr__().\n", | ||
| 204 | " \"\"\"\n", | ||
| 205 | " return f\"{self._x}:{self.__y}\"\n", | ||
| 206 | "\n", | ||
| 207 | " @property\n", | ||
| 208 | " def x(self) -> int:\n", | ||
| 209 | " \"\"\"getter property x\"\"\"\n", | ||
| 210 | " return self._x\n", | ||
| 211 | "\n", | ||
| 212 | " @property\n", | ||
| 213 | " def y(self) -> int:\n", | ||
| 214 | " \"\"\"getter property y\"\"\"\n", | ||
| 215 | " return self.__y\n", | ||
| 216 | "\n", | ||
| 217 | "\n", | ||
| 218 | "my_first_point = Point(2, 8)\n", | ||
| 219 | "\n", | ||
| 220 | "print(f\"{my_first_point}\")\n", | ||
| 221 | "print(f\"{my_first_point}\")\n", | ||
| 222 | "print(my_first_point.x)\n", | ||
| 223 | "\n", | ||
| 224 | "# we \"should not\" be accessing private and protected attributes directly\n", | ||
| 225 | "print(my_first_point._x)\n", | ||
| 226 | "\n", | ||
| 227 | "print(my_first_point.__y) # will not work\n", | ||
| 228 | "# Out: AttributeError: 'Point' object has no attribute '__y'\n", | ||
| 229 | "\n", | ||
| 230 | "# will work, but should not be used by a sane programmer:\n", | ||
| 231 | "# print(my_first_point._Point__y)" | ||
| 232 | ] | ||
| 233 | }, | ||
| 234 | { | ||
| 235 | "cell_type": "code", | ||
| 236 | "execution_count": null, | ||
| 237 | "id": "e82feefb-025d-4cbb-a8ac-ddc3cc01269c", | ||
| 238 | "metadata": {}, | ||
| 239 | "outputs": [], | ||
| 240 | "source": [ | ||
| 241 | "class Vector:\n", | ||
| 242 | " \"\"\"Basic class for representing vectors in 2D space\"\"\"\n", | ||
| 243 | "\n", | ||
| 244 | " def __init__(self, start: Point, end: Point):\n", | ||
| 245 | " self._start = start\n", | ||
| 246 | " self._end = end\n", | ||
| 247 | "\n", | ||
| 248 | " def __str__(self):\n", | ||
| 249 | " return f\"{self._start} -> {self._end}\"\n" | ||
| 250 | ] | ||
| 251 | } | ||
| 252 | ], | ||
| 253 | "metadata": { | ||
| 254 | "kernelspec": { | ||
| 255 | "display_name": "Python 3 (ipykernel)", | ||
| 256 | "language": "python", | ||
| 257 | "name": "python3" | ||
| 258 | }, | ||
| 259 | "language_info": { | ||
| 260 | "codemirror_mode": { | ||
| 261 | "name": "ipython", | ||
| 262 | "version": 3 | ||
| 263 | }, | ||
| 264 | "file_extension": ".py", | ||
| 265 | "mimetype": "text/x-python", | ||
| 266 | "name": "python", | ||
| 267 | "nbconvert_exporter": "python", | ||
| 268 | "pygments_lexer": "ipython3", | ||
| 269 | "version": "3.8.10" | ||
| 270 | } | ||
| 271 | }, | ||
| 272 | "nbformat": 4, | ||
| 273 | "nbformat_minor": 5 | ||
| 274 | } | ||
