From 66cf88dc01d79fe1d4e148f6d5371b0e649e93de Mon Sep 17 00:00:00 2001 From: Despina Adamopoulou Date: Wed, 25 Mar 2026 11:13:49 +0100 Subject: [PATCH 1/3] Add paragraph about --- 13_object_oriented_programming_advanced.ipynb | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/13_object_oriented_programming_advanced.ipynb b/13_object_oriented_programming_advanced.ipynb index cd0c03c5..98e76713 100644 --- a/13_object_oriented_programming_advanced.ipynb +++ b/13_object_oriented_programming_advanced.ipynb @@ -23,6 +23,7 @@ " - [`super()`](#super())\n", " - [Quiz on Inheritance](#Quiz-on-Inheritance)\n", " - [Exercise: Child Eye Color](#Exercise:-Child-Eye-Color)\n", + " - [The `__new__` method](#The-__new__-method)\n", " - [Abstract Classes](#Abstract-Classes)\n", " - [Quiz on Abstraction](#Quiz-on-Abstraction)\n", " - [Exercise: Banking System](#Exercise:-Banking-System)\n", @@ -549,6 +550,77 @@ " return" ] }, + { + "cell_type": "markdown", + "id": "3bb455d7", + "metadata": {}, + "source": [ + "## The `__new__` method\n", + "\n", + "Let's also take a look at the `__new__` method, as explained [here](https://www.pythontutorial.net/python-oop/python-__new__/).\n", + "\n", + "`__new__` is a static method of a class.\n", + "When you create a new instance of a class, Python actually calls `__new__` first, to create the object, and then calls `__init__` to initialize the object's attributes.\n", + "Override the `__new__` method if you want to tweak the object at creation time.\n", + "\n", + "The signature of `__new__` is:\n", + "```python\n", + "def __new__(cls, *args, **kwargs):\n", + " instance = super().__new__(cls)\n", + " return instance\n", + "```\n", + "\n", + "- `cls`: The class being instantiated (analogous to `self` in `__init__`, but refers to the class itself, not yet an instance). This is passed automatically by Python.\n", + "- `*args` / `**kwargs`: Any positional or keyword arguments that were passed to the class constructor. The same ones that will later be forwarded to `__init__`.\n", + "- `super().__new__(cls)`: Delegates the actual object allocation to the parent class (ultimately `object`). This is what physically creates and returns the new instance in memory.\n", + "- The method should the new instance. If it returns an instance of `cls`, Python will then call `__init__` on it.\n", + "\n", + "A common real-world use case is implementing the [**Singleton pattern**](https://www.geeksforgeeks.org/python/singleton-pattern-in-python-a-complete-guide/), ensuring that only one instance of a class ever exists.\n", + "By controlling object creation inside `__new__` you can return the same instance every time the class is called." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b0d9e110", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating new instance...\n", + "Returning existing instance.\n", + "a.value = 99\n", + "b.value = 99\n", + "a is b: True\n" + ] + } + ], + "source": [ + "class Singleton:\n", + " _instance = None # class-level variable to hold the single instance\n", + "\n", + " def __new__(cls, *args, **kwargs):\n", + " if cls._instance is None:\n", + " print(\"Creating new instance...\")\n", + " cls._instance = super().__new__(cls)\n", + " else:\n", + " print(\"Returning existing instance.\")\n", + " return cls._instance\n", + "\n", + " def __init__(self, value):\n", + " self.value = value\n", + "\n", + "\n", + "a = Singleton(42)\n", + "b = Singleton(99)\n", + "\n", + "print(f\"a.value = {a.value}\")\n", + "print(f\"b.value = {b.value}\")\n", + "print(f\"a is b: {a is b}\") # True because both variables point to the same object" + ] + }, { "cell_type": "markdown", "id": "39", From e960b28c85469dee88afd4be7395b971e82f3e10 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:15:14 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 13_object_oriented_programming_advanced.ipynb | 182 ++++++++---------- 1 file changed, 85 insertions(+), 97 deletions(-) diff --git a/13_object_oriented_programming_advanced.ipynb b/13_object_oriented_programming_advanced.ipynb index 98e76713..2b62f6af 100644 --- a/13_object_oriented_programming_advanced.ipynb +++ b/13_object_oriented_programming_advanced.ipynb @@ -552,7 +552,7 @@ }, { "cell_type": "markdown", - "id": "3bb455d7", + "id": "39", "metadata": {}, "source": [ "## The `__new__` method\n", @@ -581,22 +581,10 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "b0d9e110", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Creating new instance...\n", - "Returning existing instance.\n", - "a.value = 99\n", - "b.value = 99\n", - "a is b: True\n" - ] - } - ], + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], "source": [ "class Singleton:\n", " _instance = None # class-level variable to hold the single instance\n", @@ -623,7 +611,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "41", "metadata": {}, "source": [ "## Abstract Classes\n", @@ -641,7 +629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -660,7 +648,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "43", "metadata": {}, "source": [ "We mark methods of an abstract class as **virtual** – meaning that they must be overridden by each subclass that inherits from the abstract parent – using the `abstractmethod()` decorator.\n", @@ -672,7 +660,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -681,7 +669,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "45", "metadata": {}, "source": [ "Let's create two concrete subclasses of `Shape`:" @@ -690,7 +678,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -719,7 +707,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "47", "metadata": {}, "source": [ "Now we are allowed to create instances of the subclasses and also call their methods:" @@ -728,7 +716,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -743,7 +731,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "49", "metadata": {}, "source": [ "### Quiz on Abstraction" @@ -752,7 +740,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -763,7 +751,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "51", "metadata": {}, "source": [ "### Exercise: Banking System\n", @@ -814,7 +802,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -824,7 +812,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -850,7 +838,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "54", "metadata": {}, "source": [ "## Decorators\n", @@ -863,7 +851,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "55", "metadata": {}, "source": [ "### @classmethod\n", @@ -885,7 +873,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -927,7 +915,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "57", "metadata": {}, "source": [ "### @staticmethod\n", @@ -942,7 +930,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "58", "metadata": { "lines_to_next_cell": 2 }, @@ -964,7 +952,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -984,7 +972,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "60", "metadata": {}, "source": [ "### @property\n", @@ -1001,7 +989,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -1024,7 +1012,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "62", "metadata": {}, "source": [ "We can access the `area` property, just like any other class attribute.\n", @@ -1034,7 +1022,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -1043,7 +1031,7 @@ }, { "cell_type": "markdown", - "id": "62", + "id": "64", "metadata": {}, "source": [ "### Setters & Getters\n", @@ -1061,7 +1049,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -1087,7 +1075,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "66", "metadata": {}, "source": [ "Create an instance and use the getter:" @@ -1096,7 +1084,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -1106,7 +1094,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "68", "metadata": {}, "source": [ "Update the radius using the setter:" @@ -1115,7 +1103,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -1126,7 +1114,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "70", "metadata": {}, "source": [ "What happens when we enter an invalid value?" @@ -1135,7 +1123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "71", "metadata": {}, "outputs": [], "source": [ @@ -1144,7 +1132,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "72", "metadata": {}, "source": [ "Finally let's use the deleter:" @@ -1153,7 +1141,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "73", "metadata": {}, "outputs": [], "source": [ @@ -1162,7 +1150,7 @@ }, { "cell_type": "markdown", - "id": "72", + "id": "74", "metadata": {}, "source": [ "We are no longer able to access the deleted attribute:" @@ -1171,7 +1159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -1180,7 +1168,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "76", "metadata": {}, "source": [ "### Quiz on Decorators" @@ -1189,7 +1177,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "77", "metadata": {}, "outputs": [], "source": [ @@ -1200,7 +1188,7 @@ }, { "cell_type": "markdown", - "id": "76", + "id": "78", "metadata": {}, "source": [ "## Encapsulation\n", @@ -1221,7 +1209,7 @@ }, { "cell_type": "markdown", - "id": "77", + "id": "79", "metadata": {}, "source": [ "### Public\n", @@ -1230,7 +1218,7 @@ }, { "cell_type": "markdown", - "id": "78", + "id": "80", "metadata": {}, "source": [ "### Private\n", @@ -1242,7 +1230,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "81", "metadata": {}, "outputs": [], "source": [ @@ -1261,7 +1249,7 @@ }, { "cell_type": "markdown", - "id": "80", + "id": "82", "metadata": {}, "source": [ "### Protected\n", @@ -1274,7 +1262,7 @@ { "cell_type": "code", "execution_count": null, - "id": "81", + "id": "83", "metadata": {}, "outputs": [], "source": [ @@ -1293,7 +1281,7 @@ }, { "cell_type": "markdown", - "id": "82", + "id": "84", "metadata": {}, "source": [ "### Quiz on Encapsulation" @@ -1302,7 +1290,7 @@ { "cell_type": "code", "execution_count": null, - "id": "83", + "id": "85", "metadata": {}, "outputs": [], "source": [ @@ -1313,7 +1301,7 @@ }, { "cell_type": "markdown", - "id": "84", + "id": "86", "metadata": {}, "source": [ "## How to write better classes\n", @@ -1323,7 +1311,7 @@ }, { "cell_type": "markdown", - "id": "85", + "id": "87", "metadata": {}, "source": [ "### Using `dataclasses`\n", @@ -1334,7 +1322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "86", + "id": "88", "metadata": {}, "outputs": [], "source": [ @@ -1347,7 +1335,7 @@ }, { "cell_type": "markdown", - "id": "87", + "id": "89", "metadata": {}, "source": [ "A simpler way, however, would be to import `dataclass` from the `dataclasses` module.\n", @@ -1358,7 +1346,7 @@ { "cell_type": "code", "execution_count": null, - "id": "88", + "id": "90", "metadata": {}, "outputs": [], "source": [ @@ -1374,7 +1362,7 @@ }, { "cell_type": "markdown", - "id": "89", + "id": "91", "metadata": {}, "source": [ "Now, with the use of these auto generated methods, we can create an instance of the class and print a representation of the object, without any additional code.\n", @@ -1384,7 +1372,7 @@ { "cell_type": "code", "execution_count": null, - "id": "90", + "id": "92", "metadata": {}, "outputs": [], "source": [ @@ -1398,7 +1386,7 @@ }, { "cell_type": "markdown", - "id": "91", + "id": "93", "metadata": {}, "source": [ "### Using `attrs`\n", @@ -1420,7 +1408,7 @@ }, { "cell_type": "markdown", - "id": "92", + "id": "94", "metadata": {}, "source": [ "Let's rewrite the previous example, this time using `attrs`.\n", @@ -1430,7 +1418,7 @@ { "cell_type": "code", "execution_count": null, - "id": "93", + "id": "95", "metadata": {}, "outputs": [], "source": [ @@ -1453,7 +1441,7 @@ }, { "cell_type": "markdown", - "id": "94", + "id": "96", "metadata": {}, "source": [ "However, `attrs` also provides **validators**.\n", @@ -1465,7 +1453,7 @@ { "cell_type": "code", "execution_count": null, - "id": "95", + "id": "97", "metadata": {}, "outputs": [], "source": [ @@ -1489,7 +1477,7 @@ }, { "cell_type": "markdown", - "id": "96", + "id": "98", "metadata": {}, "source": [ "### Quiz on `attrs` and `dataclasses`" @@ -1498,7 +1486,7 @@ { "cell_type": "code", "execution_count": null, - "id": "97", + "id": "99", "metadata": {}, "outputs": [], "source": [ @@ -1509,7 +1497,7 @@ }, { "cell_type": "markdown", - "id": "98", + "id": "100", "metadata": {}, "source": [ "## Exercises" @@ -1518,7 +1506,7 @@ { "cell_type": "code", "execution_count": null, - "id": "99", + "id": "101", "metadata": {}, "outputs": [], "source": [ @@ -1527,7 +1515,7 @@ }, { "cell_type": "markdown", - "id": "100", + "id": "102", "metadata": {}, "source": [ "### Store Inventory\n", @@ -1567,7 +1555,7 @@ { "cell_type": "code", "execution_count": null, - "id": "101", + "id": "103", "metadata": {}, "outputs": [], "source": [ @@ -1603,7 +1591,7 @@ }, { "cell_type": "markdown", - "id": "102", + "id": "104", "metadata": {}, "source": [ "### Music Streaming Service\n", @@ -1636,7 +1624,7 @@ { "cell_type": "code", "execution_count": null, - "id": "103", + "id": "105", "metadata": {}, "outputs": [], "source": [ @@ -1686,13 +1674,13 @@ }, { "cell_type": "markdown", - "id": "104", + "id": "106", "metadata": {}, "source": [] }, { "cell_type": "markdown", - "id": "105", + "id": "107", "metadata": {}, "source": [ "### The N-body problem\n", @@ -1719,7 +1707,7 @@ }, { "cell_type": "markdown", - "id": "106", + "id": "108", "metadata": {}, "source": [ "
\n", @@ -1732,7 +1720,7 @@ { "cell_type": "code", "execution_count": null, - "id": "107", + "id": "109", "metadata": { "lines_to_next_cell": 2, "tags": [] @@ -1746,7 +1734,7 @@ }, { "cell_type": "markdown", - "id": "108", + "id": "110", "metadata": {}, "source": [ "
\n", @@ -1757,7 +1745,7 @@ }, { "cell_type": "markdown", - "id": "109", + "id": "111", "metadata": {}, "source": [ "Each of the strings output by your solution function below should be something like\n", @@ -1771,7 +1759,7 @@ { "cell_type": "code", "execution_count": null, - "id": "110", + "id": "112", "metadata": { "lines_to_next_cell": 2, "tags": [] @@ -1785,7 +1773,7 @@ }, { "cell_type": "markdown", - "id": "111", + "id": "113", "metadata": {}, "source": [ "
\n", @@ -1812,7 +1800,7 @@ }, { "cell_type": "markdown", - "id": "112", + "id": "114", "metadata": {}, "source": [ "To have a complete account of the moons' orbits, you need to compute the **total energy of the system**.\n", @@ -1855,7 +1843,7 @@ }, { "cell_type": "markdown", - "id": "113", + "id": "115", "metadata": {}, "source": [ "
\n", @@ -1868,7 +1856,7 @@ { "cell_type": "code", "execution_count": null, - "id": "114", + "id": "116", "metadata": { "lines_to_next_cell": 2, "tags": [] @@ -1882,7 +1870,7 @@ }, { "cell_type": "markdown", - "id": "115", + "id": "117", "metadata": {}, "source": [ "
\n", @@ -1895,7 +1883,7 @@ }, { "cell_type": "markdown", - "id": "116", + "id": "118", "metadata": {}, "source": [ "Here's an example of the input format:\n", @@ -1910,7 +1898,7 @@ }, { "cell_type": "markdown", - "id": "117", + "id": "119", "metadata": {}, "source": [ "
\n", @@ -1922,7 +1910,7 @@ { "cell_type": "code", "execution_count": null, - "id": "118", + "id": "120", "metadata": { "tags": [] }, From c73942ff7ea0b260031ac9c7fd64d44a3bbfca6b Mon Sep 17 00:00:00 2001 From: Edoardo Baldi Date: Thu, 26 Mar 2026 12:08:51 +0100 Subject: [PATCH 3/3] Small changes to text --- 13_object_oriented_programming_advanced.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/13_object_oriented_programming_advanced.ipynb b/13_object_oriented_programming_advanced.ipynb index 2b62f6af..7f8a6add 100644 --- a/13_object_oriented_programming_advanced.ipynb +++ b/13_object_oriented_programming_advanced.ipynb @@ -558,6 +558,7 @@ "## The `__new__` method\n", "\n", "Let's also take a look at the `__new__` method, as explained [here](https://www.pythontutorial.net/python-oop/python-__new__/).\n", + "A more technical explanation is given in the [official documentation](https://docs.python.org/3/reference/datamodel.html#object.__new__).\n", "\n", "`__new__` is a static method of a class.\n", "When you create a new instance of a class, Python actually calls `__new__` first, to create the object, and then calls `__init__` to initialize the object's attributes.\n", @@ -572,7 +573,7 @@ "\n", "- `cls`: The class being instantiated (analogous to `self` in `__init__`, but refers to the class itself, not yet an instance). This is passed automatically by Python.\n", "- `*args` / `**kwargs`: Any positional or keyword arguments that were passed to the class constructor. The same ones that will later be forwarded to `__init__`.\n", - "- `super().__new__(cls)`: Delegates the actual object allocation to the parent class (ultimately `object`). This is what physically creates and returns the new instance in memory.\n", + "- `super().__new__(cls)`: Delegates the actual object allocation to the parent class (ultimately `object`, the base class of anything in Python). This creates and returns the new instance in memory.\n", "- The method should the new instance. If it returns an instance of `cls`, Python will then call `__init__` on it.\n", "\n", "A common real-world use case is implementing the [**Singleton pattern**](https://www.geeksforgeeks.org/python/singleton-pattern-in-python-a-complete-guide/), ensuring that only one instance of a class ever exists.\n",