p2`** | **`p1.__gt__(p2)`** | **`__gt__(self, other)`** |\n",
512 | "| **Greater than or equal to** | **`p1 >= p2`** | **`p1.__ge__(p2)`** | **`__gt__(self, other)`** |"
513 | ]
514 | },
515 | {
516 | "cell_type": "code",
517 | "execution_count": null,
518 | "metadata": {},
519 | "outputs": [],
520 | "source": []
521 | }
522 | ],
523 | "metadata": {
524 | "hide_input": false,
525 | "kernelspec": {
526 | "display_name": "Python 3",
527 | "language": "python",
528 | "name": "python3"
529 | },
530 | "language_info": {
531 | "codemirror_mode": {
532 | "name": "ipython",
533 | "version": 3
534 | },
535 | "file_extension": ".py",
536 | "mimetype": "text/x-python",
537 | "name": "python",
538 | "nbconvert_exporter": "python",
539 | "pygments_lexer": "ipython3",
540 | "version": "3.8.8"
541 | },
542 | "toc": {
543 | "base_numbering": 1,
544 | "nav_menu": {},
545 | "number_sections": true,
546 | "sideBar": true,
547 | "skip_h1_title": false,
548 | "title_cell": "Table of Contents",
549 | "title_sidebar": "Contents",
550 | "toc_cell": false,
551 | "toc_position": {},
552 | "toc_section_display": true,
553 | "toc_window_display": false
554 | },
555 | "varInspector": {
556 | "cols": {
557 | "lenName": 16,
558 | "lenType": 16,
559 | "lenVar": 40
560 | },
561 | "kernels_config": {
562 | "python": {
563 | "delete_cmd_postfix": "",
564 | "delete_cmd_prefix": "del ",
565 | "library": "var_list.py",
566 | "varRefreshCmd": "print(var_dic_list())"
567 | },
568 | "r": {
569 | "delete_cmd_postfix": ") ",
570 | "delete_cmd_prefix": "rm(",
571 | "library": "var_list.r",
572 | "varRefreshCmd": "cat(var_dic_list()) "
573 | }
574 | },
575 | "types_to_exclude": [
576 | "module",
577 | "function",
578 | "builtin_function_or_method",
579 | "instance",
580 | "_Feature"
581 | ],
582 | "window_display": false
583 | }
584 | },
585 | "nbformat": 4,
586 | "nbformat_minor": 2
587 | }
588 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 milaan9
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | # 06_Python_Object_Class
23 |
24 | ## Introduction 👋
25 |
26 | Objected oriented programming as a discipline has gained a universal following among developers. Python, an in-demand programming language also follows an object-oriented programming paradigm. It deals with declaring Python classes and objects which lays the foundation of OOPs concepts. This article on “object oriented programming python” will walk you through declaring python classes, instantiating objects from them along with the four methodologies of OOPs.
27 |
28 |
29 | ## Table of contents 📋
30 |
31 | | **No.** | **Name** |
32 | | ------- | -------- |
33 | | 01 | **[Python_OOPs_Concepts](https://github.com/milaan9/06_Python_Object_Class/blob/main/001_Python_OOPs_Concepts.ipynb)** |
34 | | 02 | **[Python_Classes_and_Objects](https://github.com/milaan9/06_Python_Object_Class/blob/main/002_Python_Classes_and_Objects.ipynb)** |
35 | | 03 | **[Python_Inheritance](https://github.com/milaan9/06_Python_Object_Class/blob/main/003_Python_Inheritance.ipynb)** |
36 | | 04 | **[Python_Operator_Overloading](https://github.com/milaan9/06_Python_Object_Class/blob/main/004_Python_Operator_Overloading.ipynb)** |
37 | | | **[self_in_Python_Demystified](https://github.com/milaan9/06_Python_Object_Class/blob/main/self_in_Python_Demystified.ipynb)** |
38 |
39 |
40 | These are online **read-only** versions. However you can **`Run ▶`** all the codes **online** by clicking here ➞
41 |
42 | ---
43 |
44 | ## Frequently asked questions ❔
45 |
46 | ### How can I thank you for writing and sharing this tutorial? 🌷
47 |
48 | You can
and
Starring and Forking is free for you, but it tells me and other people that it was helpful and you like this tutorial.
49 |
50 | Go [**`here`**](https://github.com/milaan9/06_Python_Object_Class) if you aren't here already and click ➞ **`✰ Star`** and **`ⵖ Fork`** button in the top right corner. You'll be asked to create a GitHub account if you don't already have one.
51 |
52 | ---
53 |
54 | ### How can I read this tutorial without an Internet connection?
55 |
56 | 1. Go [**`here`**](https://github.com/milaan9/06_Python_Object_Class) and click the big green ➞ **`Code`** button in the top right of the page, then click ➞ [**`Download ZIP`**](https://github.com/milaan9/06_Python_Object_Class/archive/refs/heads/main.zip).
57 |
58 | 
59 |
60 | 2. Extract the ZIP and open it. Unfortunately I don't have any more specific instructions because how exactly this is done depends on which operating system you run.
61 |
62 | 3. Launch ipython notebook from the folder which contains the notebooks. Open each one of them
63 |
64 | **`Kernel > Restart & Clear Output`**
65 |
66 | This will clear all the outputs and now you can understand each statement and learn interactively.
67 |
68 | If you have git and you know how to use it, you can also clone the repository instead of downloading a zip and extracting it. An advantage with doing it this way is that you don't need to download the whole tutorial again to get the latest version of it, all you need to do is to pull with git and run ipython notebook again.
69 |
70 | ---
71 |
72 | ## Authors ✍️
73 |
74 | I'm Dr. Milaan Parmar and I have written this tutorial. If you think you can add/correct/edit and enhance this tutorial you are most welcome🙏
75 |
76 | See [github's contributors page](https://github.com/milaan9/06_Python_Object_Class/graphs/contributors) for details.
77 |
78 | If you have trouble with this tutorial please tell me about it by [Create an issue on GitHub](https://github.com/milaan9/04_Python_Functions/issues/new). and I'll make this tutorial better. This is probably the best choice if you had trouble following the tutorial, and something in it should be explained better. You will be asked to create a GitHub account if you don't already have one.
79 |
80 | If you like this tutorial, please [give it a ⭐ star](https://github.com/milaan9/06_Python_Object_Class).
81 |
82 | ---
83 |
84 | ## Licence 📜
85 |
86 | You may use this tutorial freely at your own risk. See [LICENSE](./LICENSE).
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/img/MRO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/MRO.png
--------------------------------------------------------------------------------
/img/classobj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/classobj.png
--------------------------------------------------------------------------------
/img/dnld_rep.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/dnld_rep.png
--------------------------------------------------------------------------------
/img/encap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/encap.png
--------------------------------------------------------------------------------
/img/hbi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/hbi.png
--------------------------------------------------------------------------------
/img/hi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/hi.png
--------------------------------------------------------------------------------
/img/i.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/i.png
--------------------------------------------------------------------------------
/img/instvarmeth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/instvarmeth.png
--------------------------------------------------------------------------------
/img/mli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/mli.png
--------------------------------------------------------------------------------
/img/mo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/mo.png
--------------------------------------------------------------------------------
/img/mpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/mpi.png
--------------------------------------------------------------------------------
/img/objr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/objr.png
--------------------------------------------------------------------------------
/img/polymor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/polymor.png
--------------------------------------------------------------------------------
/img/si.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/milaan9/06_Python_Object_Class/9ecc12c13ef0a434483d1e24ec6afa749fc2632f/img/si.png
--------------------------------------------------------------------------------
/self_in_Python_Demystified.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "\n",
8 | "All the IPython Notebooks in **Python Object Class** lecture series by **[Dr. Milaan Parmar](https://www.linkedin.com/in/milaanparmar/)** are available @ **[GitHub](https://github.com/milaan9/06_Python_Object_Class)**\n",
9 | ""
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {},
15 | "source": [
16 | "# self in Python, Demystified\n",
17 | "\n",
18 | "If you have been programming in Python (object-oriented programming) for some time, then you have definitely come across methods that have **`self`** as their first parameter.\n",
19 | "\n",
20 | "Let us first try to understand what this recurring **`self`** parameter is."
21 | ]
22 | },
23 | {
24 | "cell_type": "markdown",
25 | "metadata": {},
26 | "source": [
27 | "## What is `self` in Python?\n",
28 | "\n",
29 | "In object-oriented programming, whenever we define methods for a class, we use **`self`** as the first parameter in each case. Let's look at the definition of a class called **`Cat`**."
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": 1,
35 | "metadata": {
36 | "ExecuteTime": {
37 | "end_time": "2021-08-08T07:37:00.891349Z",
38 | "start_time": "2021-08-08T07:37:00.879143Z"
39 | },
40 | "scrolled": true
41 | },
42 | "outputs": [],
43 | "source": [
44 | "class Cat:\n",
45 | " def __init__(self, name, age):\n",
46 | " self.name = name\n",
47 | " self.age = age\n",
48 | "\n",
49 | " def info(self):\n",
50 | " print(f\"I am a cat. My name is {self.name}. I am {self.age} years old.\")\n",
51 | "\n",
52 | " def make_sound(self):\n",
53 | " print(\"Meow\")"
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "metadata": {},
59 | "source": [
60 | "**Explanation:**\n",
61 | "\n",
62 | "In this case all the methods, including **`__init__`**, have the first parameter as **`self`**.\n",
63 | "\n",
64 | "We know that class is a blueprint for the objects. This blueprint can be used to create multiple numbers of objects. Let's create two different objects from the above class.\n",
65 | "\n",
66 | "```python\n",
67 | "cat1 = Cat('Amelia', 3)\n",
68 | "cat2 = Cat('Bella', 6)\n",
69 | "```"
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "The **`self`** keyword is used to represent an instance (object) of the given class. In this case, the two **`Cat`** objects **`cat1`** and **`cat2`** have their own **`name`** and **`age`** attributes. If there was no **`self`** argument, the same class couldn't hold the information for both these objects.\n",
77 | "\n",
78 | "However, since the class is just a blueprint, **`self`** allows access to the attributes and methods of each object in python. This allows each object to have its own attributes and methods. Thus, even long before creating these objects, we reference the objects as **`self`** while defining the class."
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "metadata": {},
84 | "source": [
85 | "## Why is self explicitly defined everytime?\n",
86 | "\n",
87 | "Even when we understand the use of **`self`**, it may still seem odd, especially to programmers coming from other languages, that **`self`** is passed as a parameter explicitly every single time we define a method. As **The Zen of Python** goes, **\"Explicit is better than implicit\"**.\n",
88 | "\n",
89 | "So, why do we need to do this? Let's take a simple example to begin with. We have a **`Point`** class which defines a method **`distance`** to calculate the distance from the origin."
90 | ]
91 | },
92 | {
93 | "cell_type": "code",
94 | "execution_count": 2,
95 | "metadata": {
96 | "ExecuteTime": {
97 | "end_time": "2021-08-08T07:37:01.030017Z",
98 | "start_time": "2021-08-08T07:37:00.899161Z"
99 | }
100 | },
101 | "outputs": [],
102 | "source": [
103 | "class Point(object):\n",
104 | " def __init__(self,x = 0,y = 0):\n",
105 | " self.x = x\n",
106 | " self.y = y\n",
107 | "\n",
108 | " def distance(self):\n",
109 | " \"\"\"Find distance from origin\"\"\"\n",
110 | " return (self.x**2 + self.y**2) ** 0.5"
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "metadata": {},
116 | "source": [
117 | "Let us now instantiate this class and find the distance."
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": 3,
123 | "metadata": {
124 | "ExecuteTime": {
125 | "end_time": "2021-08-08T07:37:01.215566Z",
126 | "start_time": "2021-08-08T07:37:01.033439Z"
127 | }
128 | },
129 | "outputs": [
130 | {
131 | "data": {
132 | "text/plain": [
133 | "10.816653826391969"
134 | ]
135 | },
136 | "execution_count": 3,
137 | "metadata": {},
138 | "output_type": "execute_result"
139 | }
140 | ],
141 | "source": [
142 | "p1 = Point(6,9)\n",
143 | "p1.distance()"
144 | ]
145 | },
146 | {
147 | "cell_type": "markdown",
148 | "metadata": {},
149 | "source": [
150 | "In the above example, **`__init__()`** defines three parameters but we just passed two (6 and 9). Similarly **`distance()`** requires one but zero arguments were passed. Why is Python not complaining about this argument number mismatch?"
151 | ]
152 | },
153 | {
154 | "cell_type": "markdown",
155 | "metadata": {},
156 | "source": [
157 | "## What Happens Internally?\n",
158 | "\n",
159 | "**`Point.distance`** and **`p1.distance`** in the above example are different and not exactly the same."
160 | ]
161 | },
162 | {
163 | "cell_type": "code",
164 | "execution_count": 4,
165 | "metadata": {
166 | "ExecuteTime": {
167 | "end_time": "2021-08-08T07:37:01.434806Z",
168 | "start_time": "2021-08-08T07:37:01.239982Z"
169 | }
170 | },
171 | "outputs": [
172 | {
173 | "data": {
174 | "text/plain": [
175 | "function"
176 | ]
177 | },
178 | "execution_count": 4,
179 | "metadata": {},
180 | "output_type": "execute_result"
181 | }
182 | ],
183 | "source": [
184 | "type(Point.distance)"
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": 5,
190 | "metadata": {
191 | "ExecuteTime": {
192 | "end_time": "2021-08-08T07:37:01.554923Z",
193 | "start_time": "2021-08-08T07:37:01.444571Z"
194 | }
195 | },
196 | "outputs": [
197 | {
198 | "data": {
199 | "text/plain": [
200 | "method"
201 | ]
202 | },
203 | "execution_count": 5,
204 | "metadata": {},
205 | "output_type": "execute_result"
206 | }
207 | ],
208 | "source": [
209 | "type(p1.distance)"
210 | ]
211 | },
212 | {
213 | "cell_type": "markdown",
214 | "metadata": {},
215 | "source": [
216 | "We can see that the first one is a function and the second one is a method. A peculiar thing about methods (in Python) is that the object itself is passed as the first argument to the corresponding function.\n",
217 | "\n",
218 | "In the case of the above example, the method call **`p1.distance()`** is actually equivalent to **`Point.distance(p1)`**.\n",
219 | "\n",
220 | "Generally, when we call a method with some arguments, the corresponding class function is called by placing the method's object before the first argument. So, anything like **`obj.meth(args)`** becomes **`Class.meth(obj, args)`**. The calling process is automatic while the receiving process is not (its explicit).\n",
221 | "\n",
222 | "This is the reason the first parameter of a function in class must be the object itself. Writing this parameter as **`self`** is merely a convention. It is not a keyword and has no special meaning in Python. We could use other names (like **`this`**) but it is highly discouraged. Using names other than **`self`** is frowned upon by most developers and degrades the readability of the code (**Readability counts**)."
223 | ]
224 | },
225 | {
226 | "cell_type": "markdown",
227 | "metadata": {},
228 | "source": [
229 | "## Self Can Be Avoided\n",
230 | "\n",
231 | "By now you are clear that the object (instance) itself is passed along as the first argument, automatically. This implicit behavior can be avoided while making a **static** method. Consider the following simple example:"
232 | ]
233 | },
234 | {
235 | "cell_type": "code",
236 | "execution_count": 6,
237 | "metadata": {
238 | "ExecuteTime": {
239 | "end_time": "2021-08-08T07:37:01.693592Z",
240 | "start_time": "2021-08-08T07:37:01.560784Z"
241 | }
242 | },
243 | "outputs": [],
244 | "source": [
245 | "class A(object):\n",
246 | "\n",
247 | " @staticmethod\n",
248 | " def stat_meth():\n",
249 | " print(\"Look no self was passed\")"
250 | ]
251 | },
252 | {
253 | "cell_type": "markdown",
254 | "metadata": {},
255 | "source": [
256 | "Here, **`@staticmethod`** is a **[function decorator](https://github.com/milaan9/07_Python_Advanced_Topics/blob/main/004_Python_Decorators.ipynb)** that makes **`stat_meth()`** static. Let us instantiate this class and call the method."
257 | ]
258 | },
259 | {
260 | "cell_type": "code",
261 | "execution_count": 7,
262 | "metadata": {
263 | "ExecuteTime": {
264 | "end_time": "2021-08-08T07:37:01.804923Z",
265 | "start_time": "2021-08-08T07:37:01.697500Z"
266 | }
267 | },
268 | "outputs": [
269 | {
270 | "name": "stdout",
271 | "output_type": "stream",
272 | "text": [
273 | "Look no self was passed\n"
274 | ]
275 | }
276 | ],
277 | "source": [
278 | "a = A()\n",
279 | "a.stat_meth()"
280 | ]
281 | },
282 | {
283 | "cell_type": "markdown",
284 | "metadata": {},
285 | "source": [
286 | "From the above example, we can see that the implicit behavior of passing the object as the first argument was avoided while using a static method. All in all, static methods behave like the plain old functions (Since all the objects of a class share static methods)."
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": 8,
292 | "metadata": {
293 | "ExecuteTime": {
294 | "end_time": "2021-08-08T07:37:01.895743Z",
295 | "start_time": "2021-08-08T07:37:01.808830Z"
296 | }
297 | },
298 | "outputs": [
299 | {
300 | "data": {
301 | "text/plain": [
302 | "function"
303 | ]
304 | },
305 | "execution_count": 8,
306 | "metadata": {},
307 | "output_type": "execute_result"
308 | }
309 | ],
310 | "source": [
311 | "type(A.stat_meth)"
312 | ]
313 | },
314 | {
315 | "cell_type": "code",
316 | "execution_count": 9,
317 | "metadata": {
318 | "ExecuteTime": {
319 | "end_time": "2021-08-08T07:37:02.016833Z",
320 | "start_time": "2021-08-08T07:37:01.906487Z"
321 | }
322 | },
323 | "outputs": [
324 | {
325 | "data": {
326 | "text/plain": [
327 | "function"
328 | ]
329 | },
330 | "execution_count": 9,
331 | "metadata": {},
332 | "output_type": "execute_result"
333 | }
334 | ],
335 | "source": [
336 | "type(a.stat_meth)"
337 | ]
338 | },
339 | {
340 | "cell_type": "markdown",
341 | "metadata": {},
342 | "source": [
343 | "## Self Is Here To Stay\n",
344 | "\n",
345 | "The explicit **`self`** is not unique to Python. This idea was borrowed from **Modula-3**. Following is a use case where it becomes helpful.\n",
346 | "\n",
347 | "There is no explicit variable declaration in Python. They spring into action on the first assignment. The use of **`self`** makes it easier to distinguish between instance attributes (and methods) from local variables.\n",
348 | "\n",
349 | "In the first example, **`self.x`** is an instance attribute whereas **`x`** is a local variable. They are not the same and they lie in different namespaces.\n",
350 | "\n",
351 | "Many have proposed to make **`self`** a keyword in Python, like **`this`** in C++ and Java. This would eliminate the redundant use of explicit **`self`** from the formal parameter list in methods.\n",
352 | "\n",
353 | "While this idea seems promising, it is not going to happen. At least not in the near future. The main reason is backward compatibility. Here is a blog from the creator of Python himself explaining **[why the explicit self has to stay](http://neopythonic.blogspot.in/2008/10/why-explicit-self-has-to-stay.html)**."
354 | ]
355 | },
356 | {
357 | "cell_type": "markdown",
358 | "metadata": {},
359 | "source": [
360 | "## `__init__()` is not a constructor\n",
361 | "\n",
362 | "One important conclusion that can be drawn from the information so far is that the **`__init__()`** method is not a constructor. Many naive Python programmers get confused with it since **`__init__()`** gets called when we create an object.\n",
363 | "\n",
364 | "A closer inspection will reveal that the first parameter in **`__init__()`** is the object itself (object already exists). The function **`__init__()`** is called immediately **after** the object is created and is used to initialize it.\n",
365 | "\n",
366 | "Technically speaking, a constructor is a method which creates the object itself. In Python, this method is **`__new__()`**. A common signature of this method is:\n",
367 | "\n",
368 | "```python\n",
369 | "__new__(cls, *args, **kwargs)\n",
370 | "```\n",
371 | "\n",
372 | "When **`__new__()`** is called, the class itself is passed as the first argument automatically(cls).\n",
373 | "\n",
374 | "Again, like **`self`**, **`cls`** is just a naming convention. Furthermore, __*args__ and __**kwargs__ are used to take an arbitrary number of arguments during method calls in Python.\n",
375 | "\n",
376 | "Some important things to remember when implementing **`__new__()`** are:\n",
377 | "\n",
378 | "* **`__new__()`** is always called before **`__init__()`**.\n",
379 | "* First argument is the class itself which is passed implicitly.\n",
380 | "* Always return a valid object from **`__new__()`**. Not mandatory, but its main use is to create and return an object.\n",
381 | "\n",
382 | "Let's take a look at an example:"
383 | ]
384 | },
385 | {
386 | "cell_type": "code",
387 | "execution_count": 10,
388 | "metadata": {
389 | "ExecuteTime": {
390 | "end_time": "2021-08-08T07:37:02.095450Z",
391 | "start_time": "2021-08-08T07:37:02.021231Z"
392 | }
393 | },
394 | "outputs": [],
395 | "source": [
396 | "class Point(object):\n",
397 | "\n",
398 | " def __new__(cls,*args,**kwargs):\n",
399 | " print(\"From new\")\n",
400 | " print(cls)\n",
401 | " print(args)\n",
402 | " print(kwargs)\n",
403 | "\n",
404 | " # create our object and return it\n",
405 | " obj = super().__new__(cls)\n",
406 | " return obj\n",
407 | "\n",
408 | " def __init__(self,a = 0,b = 0):\n",
409 | " print(\"From init\")\n",
410 | " self.a = a\n",
411 | " self.b = b"
412 | ]
413 | },
414 | {
415 | "cell_type": "markdown",
416 | "metadata": {},
417 | "source": [
418 | "Now, let's now instantiate it."
419 | ]
420 | },
421 | {
422 | "cell_type": "code",
423 | "execution_count": 11,
424 | "metadata": {
425 | "ExecuteTime": {
426 | "end_time": "2021-08-08T07:37:02.187737Z",
427 | "start_time": "2021-08-08T07:37:02.099357Z"
428 | }
429 | },
430 | "outputs": [
431 | {
432 | "name": "stdout",
433 | "output_type": "stream",
434 | "text": [
435 | "From new\n",
436 | "\n",
437 | "(6, 9)\n",
438 | "{}\n",
439 | "From init\n"
440 | ]
441 | }
442 | ],
443 | "source": [
444 | "p2 = Point(6,9)"
445 | ]
446 | },
447 | {
448 | "cell_type": "markdown",
449 | "metadata": {},
450 | "source": [
451 | "This example illustrates that **`__new__()`** is called before **`__init__()`**. We can also see that the parameter **`cls`** in **`__new__()`** is the class itself (**`Point`**). Finally, the object is created by calling the **`__new__()`** method on **object** base class.\n",
452 | "\n",
453 | "In Python, object is the base class from which all other classes are derived. In the above example, we have done this using **[super()](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/068_Python_super%28%29.ipynb)**."
454 | ]
455 | },
456 | {
457 | "cell_type": "markdown",
458 | "metadata": {},
459 | "source": [
460 | "## Use `__new__` or `__init__`?\n",
461 | "\n",
462 | "You might have seen **`__init__()`** very often but the use of **`__new__()`** is rare. This is because most of the time you don't need to override it. Generally, **`__init__()`** is used to initialize a newly created object while **`__new__()`** is used to control the way an object is created.\n",
463 | "\n",
464 | "We can also use **`__new__()`** to initialize attributes of an object, but logically it should be inside **`__init__()`**.\n",
465 | "\n",
466 | "One practical use of **`__new__()`**, however, could be to restrict the number of objects created from a class.\n",
467 | "\n",
468 | "Suppose we wanted a class **`HexPoint`** for creating instances to represent the six vertices of a square. We can inherit from our previous class **`Point`** (the second example in this article) and use **`__new__()`** to implement this restriction. Here is an example to restrict a class to have only four instances."
469 | ]
470 | },
471 | {
472 | "cell_type": "code",
473 | "execution_count": 12,
474 | "metadata": {
475 | "ExecuteTime": {
476 | "end_time": "2021-08-08T07:37:02.279042Z",
477 | "start_time": "2021-08-08T07:37:02.192619Z"
478 | }
479 | },
480 | "outputs": [],
481 | "source": [
482 | "class HexPoint(Point):\n",
483 | " MAX_Inst = 6\n",
484 | " Inst_created = 0\n",
485 | "\n",
486 | " def __new__(cls,*args,**kwargs):\n",
487 | " if (cls.Inst_created >= cls.MAX_Inst):\n",
488 | " raise ValueError(\"Cannot create more objects\")\n",
489 | " cls.Inst_created += 1\n",
490 | " return super().__new__(cls)"
491 | ]
492 | },
493 | {
494 | "cell_type": "code",
495 | "execution_count": 13,
496 | "metadata": {
497 | "ExecuteTime": {
498 | "end_time": "2021-08-08T07:37:02.371816Z",
499 | "start_time": "2021-08-08T07:37:02.284903Z"
500 | }
501 | },
502 | "outputs": [
503 | {
504 | "name": "stdout",
505 | "output_type": "stream",
506 | "text": [
507 | "From new\n",
508 | "\n",
509 | "()\n",
510 | "{}\n",
511 | "From init\n",
512 | "From new\n",
513 | "\n",
514 | "()\n",
515 | "{}\n",
516 | "From init\n",
517 | "From new\n",
518 | "\n",
519 | "()\n",
520 | "{}\n",
521 | "From init\n",
522 | "From new\n",
523 | "\n",
524 | "()\n",
525 | "{}\n",
526 | "From init\n",
527 | "From new\n",
528 | "\n",
529 | "()\n",
530 | "{}\n",
531 | "From init\n",
532 | "From new\n",
533 | "\n",
534 | "()\n",
535 | "{}\n",
536 | "From init\n"
537 | ]
538 | }
539 | ],
540 | "source": [
541 | "p1 = HexPoint(0,0)\n",
542 | "p2 = HexPoint(1,0)\n",
543 | "p3 = HexPoint(1,1)\n",
544 | "p4 = HexPoint(0,1)\n",
545 | "p5 = HexPoint(2,2)\n",
546 | "p6 = HexPoint(2,3)"
547 | ]
548 | },
549 | {
550 | "cell_type": "code",
551 | "execution_count": 14,
552 | "metadata": {
553 | "ExecuteTime": {
554 | "end_time": "2021-08-08T07:37:02.912350Z",
555 | "start_time": "2021-08-08T07:37:02.378165Z"
556 | }
557 | },
558 | "outputs": [
559 | {
560 | "ename": "ValueError",
561 | "evalue": "Cannot create more objects",
562 | "output_type": "error",
563 | "traceback": [
564 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
565 | "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)",
566 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mp7\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mHexPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m4\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
567 | "\u001b[1;32m\u001b[0m in \u001b[0;36m__new__\u001b[1;34m(cls, *args, **kwargs)\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m__new__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mInst_created\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[0mcls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mMAX_Inst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Cannot create more objects\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 8\u001b[0m \u001b[0mcls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mInst_created\u001b[0m \u001b[1;33m+=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__new__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
568 | "\u001b[1;31mValueError\u001b[0m: Cannot create more objects"
569 | ]
570 | }
571 | ],
572 | "source": [
573 | "p7 = HexPoint(2,4)"
574 | ]
575 | },
576 | {
577 | "cell_type": "code",
578 | "execution_count": null,
579 | "metadata": {},
580 | "outputs": [],
581 | "source": []
582 | }
583 | ],
584 | "metadata": {
585 | "hide_input": false,
586 | "kernelspec": {
587 | "display_name": "Python 3",
588 | "language": "python",
589 | "name": "python3"
590 | },
591 | "language_info": {
592 | "codemirror_mode": {
593 | "name": "ipython",
594 | "version": 3
595 | },
596 | "file_extension": ".py",
597 | "mimetype": "text/x-python",
598 | "name": "python",
599 | "nbconvert_exporter": "python",
600 | "pygments_lexer": "ipython3",
601 | "version": "3.8.8"
602 | },
603 | "toc": {
604 | "base_numbering": 1,
605 | "nav_menu": {},
606 | "number_sections": true,
607 | "sideBar": true,
608 | "skip_h1_title": false,
609 | "title_cell": "Table of Contents",
610 | "title_sidebar": "Contents",
611 | "toc_cell": false,
612 | "toc_position": {},
613 | "toc_section_display": true,
614 | "toc_window_display": false
615 | },
616 | "varInspector": {
617 | "cols": {
618 | "lenName": 16,
619 | "lenType": 16,
620 | "lenVar": 40
621 | },
622 | "kernels_config": {
623 | "python": {
624 | "delete_cmd_postfix": "",
625 | "delete_cmd_prefix": "del ",
626 | "library": "var_list.py",
627 | "varRefreshCmd": "print(var_dic_list())"
628 | },
629 | "r": {
630 | "delete_cmd_postfix": ") ",
631 | "delete_cmd_prefix": "rm(",
632 | "library": "var_list.r",
633 | "varRefreshCmd": "cat(var_dic_list()) "
634 | }
635 | },
636 | "types_to_exclude": [
637 | "module",
638 | "function",
639 | "builtin_function_or_method",
640 | "instance",
641 | "_Feature"
642 | ],
643 | "window_display": false
644 | }
645 | },
646 | "nbformat": 4,
647 | "nbformat_minor": 2
648 | }
649 |
--------------------------------------------------------------------------------