summaryrefslogtreecommitdiff
path: root/notebooks/python
diff options
context:
space:
mode:
authorSimeon Simeonov2023-01-18 21:12:31 +0100
committerSimeon Simeonov2023-01-18 21:12:31 +0100
commit6dcd1727eb9d5dbfcb8f6e9599ada10d33063bac (patch)
tree57d6aa95763fd57835316cca7ce1ae33f3f19ec2 /notebooks/python
parenta33898d9db2f0e0e9b3da6f1b329b4045b65af11 (diff)
Remove some old presentations
Diffstat (limited to 'notebooks/python')
-rw-r--r--notebooks/python/python_intro.ipynb2
-rw-r--r--notebooks/python/python_oo.ipynb107
2 files changed, 79 insertions, 30 deletions
diff --git a/notebooks/python/python_intro.ipynb b/notebooks/python/python_intro.ipynb
index 3c49a14..0168839 100644
--- a/notebooks/python/python_intro.ipynb
+++ b/notebooks/python/python_intro.ipynb
@@ -1253,7 +1253,7 @@
1253 "name": "python", 1253 "name": "python",
1254 "nbconvert_exporter": "python", 1254 "nbconvert_exporter": "python",
1255 "pygments_lexer": "ipython3", 1255 "pygments_lexer": "ipython3",
1256 "version": "3.10.8" 1256 "version": "3.11.1"
1257 } 1257 }
1258 }, 1258 },
1259 "nbformat": 4, 1259 "nbformat": 4,
diff --git a/notebooks/python/python_oo.ipynb b/notebooks/python/python_oo.ipynb
index b56aa26..1c655d0 100644
--- a/notebooks/python/python_oo.ipynb
+++ b/notebooks/python/python_oo.ipynb
@@ -179,7 +179,8 @@
179 "\"The Zen of Python\" (import this) is still relevant and should be followed :)\n", 179 "\"The Zen of Python\" (import this) is still relevant and should be followed :)\n",
180 "\n", 180 "\n",
181 "\"Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.\" - PEP484\n", 181 "\"Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.\" - PEP484\n",
182 "\n" 182 "\n",
183 "Class (type) names should normally use the *CapWords* convention - CapitalizedWords (or *CapWords*, or *CamelCase*). This is also sometimes known as *StudlyCaps*. ASCII characters and English names only!"
183 ] 184 ]
184 }, 185 },
185 { 186 {
@@ -225,17 +226,17 @@
225 "name": "stdout", 226 "name": "stdout",
226 "output_type": "stream", 227 "output_type": "stream",
227 "text": [ 228 "text": [
228 "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5630542810> with id 140008154736656 from <class '__main__.Car'> with id 94904583728688\"\n", 229 "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7add96f10> with id 140495591927568 from <class '__main__.Car'> with id 94628241379728\"\n",
229 "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5633ec8190> with id 140008215052688 from <class '__main__.Car'> with id 94904583728688\"\n", 230 "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7aef459d0> with id 140495610468816 from <class '__main__.Car'> with id 94628241379728\"\n",
230 "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272, id(car1.__init__) = 140008154735680\n", 231 "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184, id(car1.get_obj_info_str) = 140495591927808\n",
231 "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140008154734272, id(car2.__init__) = 140008154736768\n", 232 "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140495591909184, id(car2.get_obj_info_str) = 140495591927936\n",
232 "id(Car.cls_extras) = 140008154734272, id(Car.__init__) = 140008154484864\n", 233 "id(Car.cls_extras) = 140495591909184, id(Car.get_obj_info_str) = 140495591682944\n",
233 "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272\n", 234 "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184\n",
234 "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140008154734272\n", 235 "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140495591909184\n",
235 "True\n", 236 "True\n",
236 "hasattr(car1, 'import_tax_paid') = True\n", 237 "hasattr(car1, 'import_tax_paid') = True\n",
237 "hasattr(car2, 'import_tax_paid') = False\n", 238 "hasattr(car2, 'import_tax_paid') = False\n",
238 "id(car1.__class__) = 94904583728688, id(car2.__class__) = 94904583728688, id(Car) = 94904583728688\n" 239 "id(car1.__class__) = 94628241379728, id(car2.__class__) = 94628241379728, id(Car) = 94628241379728\n"
239 ] 240 ]
240 } 241 }
241 ], 242 ],
@@ -244,6 +245,7 @@
244 "\n", 245 "\n",
245 " cls_extras = [] # class attribute - shared by all instances\n", 246 " cls_extras = [] # class attribute - shared by all instances\n",
246 " # cls_extras: list = [] is also possible\n", 247 " # cls_extras: list = [] is also possible\n",
248 " car_count = 0\n",
247 "\n", 249 "\n",
248 " def __init__(self, model: str, reg_nr: str, extras: list):\n", 250 " def __init__(self, model: str, reg_nr: str, extras: list):\n",
249 " \"\"\"\n", 251 " \"\"\"\n",
@@ -262,24 +264,40 @@
262 "\n", 264 "\n",
263 " # self.cls_extras = extras # will create a *NEW instance attribute* called 'cls_extras'\n", 265 " # self.cls_extras = extras # will create a *NEW instance attribute* called 'cls_extras'\n",
264 " \n", 266 " \n",
267 " Car.car_count += 1 # increase Car.car_count by 1 every time a car is created \n",
268 " \n",
269 " def __del__(self):\n",
270 " \"\"\"\n",
271 " Called when the instance is about to be destroyed.\n",
272 "\n",
273 " This is also called a finalizer or (improperly) a destructor.\n",
274 " \"\"\"\n",
275 " Car.car_count -= 1 # decrease by 1 every time a Car object is destroyed\n",
276 "\n",
265 " def get_obj_info_str(self): # regular method that becomes instance attribute\n", 277 " def get_obj_info_str(self): # regular method that becomes instance attribute\n",
266 " return f\"I am {self} with id {id(self)} from {self.__class__} with id {id(self.__class__)}\"\n", 278 " return f\"I am {self} with id {id(self)} from {self.__class__} with id {id(self.__class__)}\"\n",
267 "\n", 279 "\n",
268 "car1 = Car(\"BMW\", \"EC76183\", [\"GPSnav\", \"Sound system\"])\n", 280 "car1 = Car(\"BMW\", \"EC76183\", [\"GPSnav\", \"Sound system\"])\n",
269 "car2 = Car(\"Scoda\", \"BD77655\", [\"GPSnav\"])\n", 281 "car2 = Car(\"Scoda\", \"BD77655\", [\"GPSnav\"])\n",
270 "\n", 282 "\n",
271 "print(f\"{car1.get_obj_info_str() = }\")\n", 283 "print(f\"{car1.get_obj_info_str() = }\") # same as calling Car.get_object_info_str(car1)\n",
272 "print(f\"{car2.get_obj_info_str() = }\")\n", 284 "print(f\"{car2.get_obj_info_str() = }\")\n",
273 "\n", 285 "\n",
274 "# default behavior for `object.attr`:\n", 286 "# default behavior for `object.attr`:\n",
275 "# getter:\n", 287 "# getter:\n",
276 "# - checks if 'attr' is an instance attribute\n", 288 "# - checks if 'attr' is an instance attribute\n",
277 "# - checks if 'attr' is a class attribute\n", 289 "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n",
278 "# - raises AttributeError\n", 290 "# - raises AttributeError\n",
279 "# setter: (re)defines an instance attribute\n", 291 "# setter:\n",
280 "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.__init__) = }\")\n", 292 "# - (re)defines an instance attribute\n",
281 "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.__init__) = }\")\n", 293 "\n",
282 "print(f\"{id(Car.cls_extras) = }, {id(Car.__init__) = }\")\n", 294 "# Details not covered in this course:\n",
295 "# Car.x is translated to Car.__dict__[\"x\"] (...through the MRO)\n",
296 "# car1.x is translated to car1.__dict__[\"x\"] (if not found ... Car.x (see above), if not found car1.__getattr__(\"x\") is called)\n",
297 "\n",
298 "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.get_obj_info_str) = }\")\n",
299 "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.get_obj_info_str) = }\")\n",
300 "print(f\"{id(Car.cls_extras) = }, {id(Car.get_obj_info_str) = }\")\n",
283 "car1.cls_extras.extend(car1.extras)\n", 301 "car1.cls_extras.extend(car1.extras)\n",
284 "# car1.cls_extras = car1.extras # N.B. This will create a *NEW instance attribute* called 'cls_extras'\n", 302 "# car1.cls_extras = car1.extras # N.B. This will create a *NEW instance attribute* called 'cls_extras'\n",
285 "print(f\"{car1.cls_extras = }, {id(car1.cls_extras) = }\")\n", 303 "print(f\"{car1.cls_extras = }, {id(car1.cls_extras) = }\")\n",
@@ -289,7 +307,7 @@
289 "car1.import_tax_paid = True # 'import_tax_paid' will only be present in car1\n", 307 "car1.import_tax_paid = True # 'import_tax_paid' will only be present in car1\n",
290 "print(car1.import_tax_paid)\n", 308 "print(car1.import_tax_paid)\n",
291 "print(f\"{hasattr(car1, 'import_tax_paid') = }\") # Out: True\n", 309 "print(f\"{hasattr(car1, 'import_tax_paid') = }\") # Out: True\n",
292 "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False\n", 310 "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False - no 'import_tax_paid' attribute in object car2\n",
293 "\n", 311 "\n",
294 "# .__class__ instance attribute pointing to its class (type) will be created\n", 312 "# .__class__ instance attribute pointing to its class (type) will be created\n",
295 "print(f\"{id(car1.__class__) = }, {id(car2.__class__) = }, {id(Car) = }\")" 313 "print(f\"{id(car1.__class__) = }, {id(car2.__class__) = }, {id(Car) = }\")"
@@ -705,6 +723,7 @@
705 "name": "stdout", 723 "name": "stdout",
706 "output_type": "stream", 724 "output_type": "stream",
707 "text": [ 725 "text": [
726 "2:2\n",
708 "__mul__ called\n", 727 "__mul__ called\n",
709 "vector * 3 = Vector(start=Point(x=2, y=2), end=Point(x=12, y=18))\n", 728 "vector * 3 = Vector(start=Point(x=2, y=2), end=Point(x=12, y=18))\n",
710 "__rmul__ called\n", 729 "__rmul__ called\n",
@@ -771,6 +790,7 @@
771 " # __rmul__ = __mul__\n", 790 " # __rmul__ = __mul__\n",
772 "\n", 791 "\n",
773 "vector = Vector(Point(2, 2), Point(4, 6))\n", 792 "vector = Vector(Point(2, 2), Point(4, 6))\n",
793 "print(f\"{vector['start']}\")\n",
774 "print(f\"{vector * 3 = }\")\n", 794 "print(f\"{vector * 3 = }\")\n",
775 "print(f\"{3 * vector = }\")" 795 "print(f\"{3 * vector = }\")"
776 ] 796 ]
@@ -863,7 +883,7 @@
863 "id": "d1d17f26-de6e-4510-b0f0-3ae02761ae24", 883 "id": "d1d17f26-de6e-4510-b0f0-3ae02761ae24",
864 "metadata": {}, 884 "metadata": {},
865 "source": [ 885 "source": [
866 "# Example 2\n", 886 "# Example 3\n",
867 "\n", 887 "\n",
868 "We will create a small and incomplete Animal class hierarchy" 888 "We will create a small and incomplete Animal class hierarchy"
869 ] 889 ]
@@ -973,7 +993,7 @@
973 " super().__init__(\n", 993 " super().__init__(\n",
974 " weight=weight, tooth_replacement=True, alive=alive, **kwargs\n", 994 " weight=weight, tooth_replacement=True, alive=alive, **kwargs\n",
975 " )\n", 995 " )\n",
976 " self_conservation_status = conservation_status\n", 996 " self._conservation_status = conservation_status\n",
977 "\n", 997 "\n",
978 "\n", 998 "\n",
979 "class Cow(Mammal):\n", 999 "class Cow(Mammal):\n",
@@ -1104,6 +1124,7 @@
1104 " \"\"\"\n", 1124 " \"\"\"\n",
1105 "\n", 1125 "\n",
1106 "\n", 1126 "\n",
1127 "# not good! The user of JSONMixin should not have to explicitly know about / use DictMixin\n",
1107 "class JSONConvertableTigerShark(TigerShark, DictMixin, JSONMixin):\n", 1128 "class JSONConvertableTigerShark(TigerShark, DictMixin, JSONMixin):\n",
1108 " \"\"\"A tiger shark class that provides JSON conversion functionality\"\"\"\n", 1129 " \"\"\"A tiger shark class that provides JSON conversion functionality\"\"\"\n",
1109 "\n", 1130 "\n",
@@ -1143,14 +1164,7 @@
1143 "MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C.\n", 1164 "MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C.\n",
1144 "\n", 1165 "\n",
1145 "Not all classes admit a linearization. There are cases, in complicated hierarchies, where it is not possible to derive a class such that its linearization respects all the desired properties. \n", 1166 "Not all classes admit a linearization. There are cases, in complicated hierarchies, where it is not possible to derive a class such that its linearization respects all the desired properties. \n",
1146 "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then.\n", 1167 "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then."
1147 "\n",
1148 "\n",
1149 "## The Liskov substitution principle (LSP):\n",
1150 "\n",
1151 "Functions that use references to base class objects must be able to use objects of derived classes without knowing it.\n",
1152 "\n",
1153 "In other words: an object (such as a class) may be replaced by a sub-object (such as a class that extends the first class) without breaking the program.\n"
1154 ] 1168 ]
1155 }, 1169 },
1156 { 1170 {
@@ -1233,6 +1247,14 @@
1233 ] 1247 ]
1234 }, 1248 },
1235 { 1249 {
1250 "cell_type": "markdown",
1251 "id": "875e7ffd-4feb-4ef0-b912-0a939c6c2dd1",
1252 "metadata": {},
1253 "source": [
1254 "... **N.B.** The most important thing to note here is that if `super()` is used in `Grandfather1` for this MRO (f.i. object of type `Simeon`) , none of its parents or siblings is called, but `Father` that was probably \"not even born\" when `Grandfather1` was created. :)"
1255 ]
1256 },
1257 {
1236 "cell_type": "code", 1258 "cell_type": "code",
1237 "execution_count": 12, 1259 "execution_count": 12,
1238 "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", 1260 "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba",
@@ -1274,7 +1296,6 @@
1274 " \"\"\"Produces JSON content for production plans\"\"\"\n", 1296 " \"\"\"Produces JSON content for production plans\"\"\"\n",
1275 " \n", 1297 " \n",
1276 " def to_json(self, **kwargs):\n", 1298 " def to_json(self, **kwargs):\n",
1277 " # super().get_data(**kwargs)\n",
1278 " self.get_data(**kwargs)\n", 1299 " self.get_data(**kwargs)\n",
1279 " prod_plans = self.extract_production_plans(**kwargs)\n", 1300 " prod_plans = self.extract_production_plans(**kwargs)\n",
1280 " return json.dumps(self.__do_some_magic(prod_plans), indent=kwargs.get(\"indent\", 2))\n", 1301 " return json.dumps(self.__do_some_magic(prod_plans), indent=kwargs.get(\"indent\", 2))\n",
@@ -1295,7 +1316,10 @@
1295 "\n", 1316 "\n",
1296 "\n", 1317 "\n",
1297 "cool_ts_proprocessor = CoolTSPreprocessor()\n", 1318 "cool_ts_proprocessor = CoolTSPreprocessor()\n",
1298 "print(cool_ts_proprocessor.to_json(indent=4))" 1319 "print(cool_ts_proprocessor.to_json(indent=4))\n",
1320 "\n",
1321 "# but we want to test CoolTSPreprocessor without the annoying ConnectionManager.get_data method being used\n",
1322 "# simply (temporary or canditionally) replacing ConnectionManager with MockConnectionManager will go against the O in SOLID"
1299 ] 1323 ]
1300 }, 1324 },
1301 { 1325 {
@@ -1311,6 +1335,7 @@
1311 "Using mock data\n", 1335 "Using mock data\n",
1312 "Extracting production plans from data\n", 1336 "Extracting production plans from data\n",
1313 "some magic is happening here...\n", 1337 "some magic is happening here...\n",
1338 "{}\n",
1314 "(<class '__main__.MockedButStillCoolTSPreprocessor'>, <class '__main__.CoolTSPreprocessor'>, <class '__main__.MockConnectionManager'>, <class '__main__.ConnectionManager'>, <class 'object'>)\n" 1339 "(<class '__main__.MockedButStillCoolTSPreprocessor'>, <class '__main__.CoolTSPreprocessor'>, <class '__main__.MockConnectionManager'>, <class '__main__.ConnectionManager'>, <class 'object'>)\n"
1315 ] 1340 ]
1316 } 1341 }
@@ -1320,12 +1345,36 @@
1320 " pass\n", 1345 " pass\n",
1321 "\n", 1346 "\n",
1322 "mocked_but_still_cool_ts_preprocessor = MockedButStillCoolTSPreprocessor()\n", 1347 "mocked_but_still_cool_ts_preprocessor = MockedButStillCoolTSPreprocessor()\n",
1323 "mocked_but_still_cool_ts_preprocessor.to_json(indent=4)\n", 1348 "print(mocked_but_still_cool_ts_preprocessor.to_json(indent=4))\n",
1324 "print(MockedButStillCoolTSPreprocessor.__mro__)" 1349 "print(MockedButStillCoolTSPreprocessor.__mro__)"
1325 ] 1350 ]
1326 }, 1351 },
1327 { 1352 {
1328 "cell_type": "markdown", 1353 "cell_type": "markdown",
1354 "id": "738b9084-27b9-4837-a927-e2087267d483",
1355 "metadata": {},
1356 "source": [
1357 "# Quick tips and final words\n",
1358 "\n",
1359 "- Avoid the classical pitfall: \"I am solving simple problems by simple means (the way I am used to) and could probably use OO when solving more complex problems\"! Solving simple problems is the best way to get used to the OO way of thinking / designing code.\n",
1360 "\n",
1361 "- Take some time to analyze and design your solution and discuss it with other team members and more experienced programmers. Do not rely only on QA (merge requests)!\n",
1362 "\n",
1363 "- Learn to think in terms of objects, like in real world objects (f.i. Car, Animal, TimeSeries, ProductionPlan, DataSource...)!\n",
1364 "\n",
1365 "- Learn to think in terms of abstract classes (types): Classes that will only serve as a foundation to their child classes, but will themselves never be directly instanciated!\n",
1366 "\n",
1367 "- Do not create \"thin\" classes / objects that simply containin some relevant attributes, protected by corresponding properties! The entire functionality (business logic) of the object should be encapsulated there.\n",
1368 "\n",
1369 "- Always make sure that you are not violating some of the SOLID principles (and others described above)! Run \"mental tests\" to test your current design!\n",
1370 "\n",
1371 "- Consider composition (\"has-a\" relationship) before considering inhiritance (\"is-a\" relationship)!\n",
1372 "\n",
1373 "- Read and learn from free software projects written in Python (f.i. on github.com)"
1374 ]
1375 },
1376 {
1377 "cell_type": "markdown",
1329 "id": "540de317-ba77-4f5c-97ba-f1c0bec071fb", 1378 "id": "540de317-ba77-4f5c-97ba-f1c0bec071fb",
1330 "metadata": {}, 1379 "metadata": {},
1331 "source": [ 1380 "source": [