├── 001_Python_OOPs_Concepts.ipynb ├── 002_Python_Classes_and_Objects.ipynb ├── 003_Python_Inheritance.ipynb ├── 004_Python_Operator_Overloading.ipynb ├── LICENSE ├── README.md ├── img ├── MRO.png ├── classobj.png ├── dnld_rep.png ├── encap.png ├── hbi.png ├── hi.png ├── i.png ├── instvarmeth.png ├── mli.png ├── mo.png ├── mpi.png ├── objr.png ├── polymor.png └── si.png └── self_in_Python_Demystified.ipynb /001_Python_OOPs_Concepts.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 | "# Python OOPs Concepts\n", 17 | "\n", 18 | "In this class, you’ll learn about Object-Oriented Programming (OOP) in Python and its fundamental concept with the help of examples." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Object Oriented Programming\n", 26 | "\n", 27 | "Python is a multi-paradigm programming language. It supports different programming approaches.\n", 28 | "\n", 29 | "One of the popular approaches to solve a programming problem is by creating objects. This is known as Object-Oriented Programming (OOP).\n", 30 | "\n", 31 | "Object-oriented programming (OOP) is a programming paradigm based on the concept of **“objects”**. The object contains both data and code: Data in the form of properties (often known as attributes), and code, in the form of methods (actions object can perform).\n", 32 | "\n", 33 | "An object has two characteristics:\n", 34 | "\n", 35 | "* attributes\n", 36 | "* behavior\n", 37 | "\n", 38 | "For example:\n", 39 | "\n", 40 | "A parrot is can be an object,as it has the following properties:\n", 41 | "\n", 42 | "* name, age, color as attributes\n", 43 | "* singing, dancing as behavior\n", 44 | "\n", 45 | "The concept of OOP in Python focuses on creating reusable code. This concept is also known as DRY (Don't Repeat Yourself).\n", 46 | "\n", 47 | "In Python, the concept of OOP follows some basic principles:" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "### Class \n", 55 | "\n", 56 | "In Python, everything is an object. A class is a blueprint for the object. To create an object we require a model or plan or blueprint which is nothing but class.\n", 57 | "\n", 58 | "We create class to create an object. A class is like an object constructor, or a \"blueprint\" for creating objects. We instantiate a class to create an object. The class defines attributes and the behavior of the object, while the object, on the other hand, represents the class.\n", 59 | "\n", 60 | "**Class represents the properties (attribute) and action (behavior) of the object. Properties represent variables, and actions are represented by the methods. Hence class contains both variables and methods.**\n", 61 | "\n", 62 | "We can think of class as a sketch of a parrot with labels. It contains all the details about the name, colors, size etc. Based on these descriptions, we can study about the parrot. Here, a parrot is an object.\n", 63 | "\n", 64 | "**Syntax:**\n", 65 | "\n", 66 | "```python\n", 67 | "class classname:\n", 68 | " '''documentation string'''\n", 69 | " class_suite\n", 70 | "```\n", 71 | "* **Documentation string:** represent a description of the class. It is optional.\n", 72 | "* **class_suite:** class suite contains component statements, variables, methods, functions, attributes.\n", 73 | "\n", 74 | "The example for class of parrot can be :\n", 75 | "\n", 76 | "```python\n", 77 | "class Parrot:\n", 78 | " pass\n", 79 | "```\n", 80 | "\n", 81 | "Here, we use the **`class`** keyword to define an empty class **`Parrot`**. From class, we construct instances. An instance is a specific object created from a particular class.\n", 82 | "\n", 83 | "```python\n", 84 | "class Person:\n", 85 | " pass\n", 86 | "print(Person)\n", 87 | "```" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 1, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "name": "stdout", 97 | "output_type": "stream", 98 | "text": [ 99 | "\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "# Creating a class\n", 105 | "\n", 106 | "class Person:\n", 107 | " pass\n", 108 | "print(Person)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "### Object \n", 116 | "\n", 117 | "The physical existence of a class is nothing but an object. In other words, the object is an entity that has a state and behavior. \n", 118 | "\n", 119 | "Therefore, an object (instance) is an instantiation of a class. So, when class is defined, only the description for the object is defined. Therefore, no memory or storage is allocated.\n", 120 | "\n", 121 | "**Syntax:**\n", 122 | "\n", 123 | "```python\n", 124 | "reference_variable = classname()\n", 125 | "```\n", 126 | "\n", 127 | "The example for object of parrot class can be:\n", 128 | "\n", 129 | "```python\n", 130 | "obj = Parrot()\n", 131 | "```\n", 132 | "\n", 133 | "Here, **`obj`** is an **`object`** of class Parrot.\n", 134 | "\n", 135 | "Suppose we have details of parrots. Now, we are going to show how to build the class and objects of parrots.\n", 136 | "\n", 137 | "```python\n", 138 | "p = Person()\n", 139 | "print(p)\n", 140 | "```" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 2, 146 | "metadata": { 147 | "scrolled": true 148 | }, 149 | "outputs": [ 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "<__main__.Person object at 0x0000021E45765B20>\n" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "# Example 1: We can create an object by calling the class\n", 160 | "\n", 161 | "p = Person()\n", 162 | "print(p)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 3, 168 | "metadata": { 169 | "ExecuteTime": { 170 | "end_time": "2021-06-19T09:00:01.298252Z", 171 | "start_time": "2021-06-19T09:00:01.283605Z" 172 | } 173 | }, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "Welcome to Dr. Milaan Parmar's class on Python Programming\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "# Example 2: Creating Class and Object in Python\n", 185 | "\n", 186 | "class Student:\n", 187 | " \"\"\"This is student class with data\"\"\" \n", 188 | " def learn(self): # A sample method\n", 189 | " print(\"Welcome to Dr. Milaan Parmar's class on Python Programming\")\n", 190 | "\n", 191 | "stud = Student() # creating object\n", 192 | "stud.learn() # Calling method\n", 193 | "\n", 194 | "# Output: Welcome to Dr. Milaan Parmar's class on Python Programming" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "## Class Constructor\n", 202 | "\n", 203 | "In the examples above, we have created an object from the **`Person`** class. However, a class without a constructor is not really useful in real applications. Let us use constructor function to make our class more useful. Like the constructor function in Java or JavaScript, Python has also a built-in **`__init__()`** constructor function. The **`__init__()`** constructor function has **`self`** parameter which is a reference to the current instance of the class." 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 4, 209 | "metadata": { 210 | "scrolled": true 211 | }, 212 | "outputs": [ 213 | { 214 | "name": "stdout", 215 | "output_type": "stream", 216 | "text": [ 217 | "Milaan\n", 218 | "<__main__.Person object at 0x0000021E45765DC0>\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "class Person:\n", 224 | " def __init__ (self, name):\n", 225 | " # self allows to attach parameter to the class\n", 226 | " self.name =name\n", 227 | "\n", 228 | "p = Person('Milaan')\n", 229 | "print(p.name)\n", 230 | "print(p)" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "Let us add more parameters to the constructor function." 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 5, 243 | "metadata": { 244 | "scrolled": true 245 | }, 246 | "outputs": [ 247 | { 248 | "name": "stdout", 249 | "output_type": "stream", 250 | "text": [ 251 | "Milaan\n", 252 | "Parmar\n", 253 | "96\n", 254 | "England\n", 255 | "London\n" 256 | ] 257 | } 258 | ], 259 | "source": [ 260 | "# Example 1: add more parameters to the constructor function.\n", 261 | "\n", 262 | "class Person:\n", 263 | " def __init__(self, firstname, lastname, age, country, city):\n", 264 | " self.firstname = firstname\n", 265 | " self.lastname = lastname\n", 266 | " self.age = age\n", 267 | " self.country = country\n", 268 | " self.city = city\n", 269 | "\n", 270 | "p = Person('Milaan', 'Parmar', 96, 'England', 'London')\n", 271 | "print(p.firstname)\n", 272 | "print(p.lastname)\n", 273 | "print(p.age)\n", 274 | "print(p.country)\n", 275 | "print(p.city)" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "## Instance Variables and Methods\n", 283 | "\n", 284 | "If the value of a variable varies from object to object, then such variables are called instance variables. For every object, a separate copy of the instance variable will be created.\n", 285 | "\n", 286 | "When we create classes in Python, instance methods are used regularly. we need to create an object to execute the block of code or action defined in the instance method.\n", 287 | "\n", 288 | "We can access the instance variable and methods using the object. Use dot (**`.`**) operator to access instance variables and methods.\n", 289 | "\n", 290 | "In Python, working with an instance variable and method, we use the **`self`** keyword. When we use the **`self`** keyword as a parameter to a method or with a variable name is called the instance itself.\n", 291 | "\n", 292 | ">**Note:** Instance variables are used within the instance method" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 6, 298 | "metadata": { 299 | "ExecuteTime": { 300 | "end_time": "2021-06-19T09:00:03.112688Z", 301 | "start_time": "2021-06-19T09:00:03.099018Z" 302 | } 303 | }, 304 | "outputs": [ 305 | { 306 | "name": "stdout", 307 | "output_type": "stream", 308 | "text": [ 309 | "Name is: Arthur and percentage is: 90\n" 310 | ] 311 | } 312 | ], 313 | "source": [ 314 | "# Example 2: Creating Class and Object in Python\n", 315 | "\n", 316 | "class Student:\n", 317 | " def __init__(self, name, percentage):\n", 318 | " self.name = name\n", 319 | " self.percentage = percentage\n", 320 | "\n", 321 | " def show(self):\n", 322 | " print(\"Name is:\", self.name, \"and percentage is:\", self.percentage)\n", 323 | "\n", 324 | " \n", 325 | "stud = Student(\"Arthur\", 90)\n", 326 | "stud.show() \n", 327 | "\n", 328 | "# Output Name is: Arthur and percentage is: 90" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "
\n", 336 | "\n", 337 | "
" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 7, 343 | "metadata": { 344 | "ExecuteTime": { 345 | "end_time": "2021-06-19T09:00:06.483747Z", 346 | "start_time": "2021-06-19T09:00:06.468123Z" 347 | } 348 | }, 349 | "outputs": [ 350 | { 351 | "name": "stdout", 352 | "output_type": "stream", 353 | "text": [ 354 | "Blu is a bird\n", 355 | "Woo is also a bird\n", 356 | "Blu is 10 years old\n", 357 | "Woo is 15 years old\n" 358 | ] 359 | } 360 | ], 361 | "source": [ 362 | "# Example 3: Creating Class and Object in Python\n", 363 | "\n", 364 | "class Parrot:\n", 365 | " species = \"bird\" # class attribute\n", 366 | " def __init__(self, name, age): # instance attribute\n", 367 | " self.name = name\n", 368 | " self.age = age\n", 369 | "\n", 370 | "# instantiate the Parrot class\n", 371 | "blu = Parrot(\"Blu\", 10)\n", 372 | "woo = Parrot(\"Woo\", 15)\n", 373 | "\n", 374 | "# access the class attributes\n", 375 | "print(\"Blu is a {}\".format(blu.__class__.species))\n", 376 | "print(\"Woo is also a {}\".format(woo.__class__.species))\n", 377 | "\n", 378 | "# access the instance attributes\n", 379 | "print(\"{} is {} years old\".format( blu.name, blu.age))\n", 380 | "print(\"{} is {} years old\".format( woo.name, woo.age))" 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "metadata": {}, 386 | "source": [ 387 | "**Explanation**:\n", 388 | "\n", 389 | "In the above program, we created a class with the name **`Parrot`**. Then, we define attributes. The attributes are a characteristic of an object.\n", 390 | "\n", 391 | "These attributes are defined inside the **`__init__`** method of the class. It is the initializer method that is first run as soon as the object is created.\n", 392 | "\n", 393 | "Then, we create instances of the **`Parrot`** class. Here, **`blu`** and **`woo`** are references (value) to our new objects.\n", 394 | "\n", 395 | "We can access the class attribute using **`__class__.species`**. Class attributes are the same for all instances of a class. Similarly, we access the instance attributes using **`blu.name`** and **`blu.age`**. However, instance attributes are different for every instance of a class.\n", 396 | "\n", 397 | "To learn more about classes and objects, go to [**Python Classes and Objects**](https://github.com/milaan9/06_Python_Object_Class/blob/main/002_Python_Classes_and_Objects.ipynb)" 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "metadata": {}, 403 | "source": [ 404 | "### Object Method\n", 405 | "\n", 406 | "Object Methods are functions defined inside the body of a class. They are used to define the behaviors of an object.\n", 407 | "\n", 408 | "Objects can have methods. The methods are functions which belong to the object." 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 8, 414 | "metadata": {}, 415 | "outputs": [ 416 | { 417 | "name": "stdout", 418 | "output_type": "stream", 419 | "text": [ 420 | "Milaan Parmar is 96 years old. He lives in London, England\n" 421 | ] 422 | } 423 | ], 424 | "source": [ 425 | "# Example 1:\n", 426 | "\n", 427 | "class Person:\n", 428 | " def __init__(self, firstname, lastname, age, country, city):\n", 429 | " self.firstname = firstname\n", 430 | " self.lastname = lastname\n", 431 | " self.age = age\n", 432 | " self.country = country\n", 433 | " self.city = city\n", 434 | " def person_info(self):\n", 435 | " return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}'\n", 436 | "\n", 437 | "p = Person('Milaan', 'Parmar', 96, 'England', 'London')\n", 438 | "print(p.person_info())" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 9, 444 | "metadata": { 445 | "ExecuteTime": { 446 | "end_time": "2021-06-19T09:00:11.054497Z", 447 | "start_time": "2021-06-19T09:00:11.034969Z" 448 | } 449 | }, 450 | "outputs": [ 451 | { 452 | "name": "stdout", 453 | "output_type": "stream", 454 | "text": [ 455 | "Blu sings 'Happy'\n", 456 | "Blu is now dancing\n" 457 | ] 458 | } 459 | ], 460 | "source": [ 461 | "# Example 2: Creating Object Methods in Python\n", 462 | "\n", 463 | "class Parrot:\n", 464 | " \n", 465 | " # instance attributes\n", 466 | " def __init__(self, name, age):\n", 467 | " self.name = name\n", 468 | " self.age = age\n", 469 | " \n", 470 | " # instance method\n", 471 | " def sing(self, song):\n", 472 | " return \"{} sings {}\".format(self.name, song)\n", 473 | "\n", 474 | " def dance(self):\n", 475 | " return \"{} is now dancing\".format(self.name)\n", 476 | "\n", 477 | "# instantiate the object\n", 478 | "blu = Parrot(\"Blu\", 10)\n", 479 | "\n", 480 | "# call our instance methods\n", 481 | "print(blu.sing(\"'Happy'\"))\n", 482 | "print(blu.dance())" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "**Explanation**:\n", 490 | "\n", 491 | "In the above program, we define two methods i.e **`sing()`** and **`dance()`**. These are called instance methods because they are called on an instance object i.e **`blu`**." 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "## Object Default Methods\n", 499 | "\n", 500 | "Sometimes, you may want to have a default values for your object methods. If we give default values for the parameters in the constructor, we can avoid errors when we call or instantiate our class without parameters. Let's see how it looks:" 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": 10, 506 | "metadata": {}, 507 | "outputs": [ 508 | { 509 | "name": "stdout", 510 | "output_type": "stream", 511 | "text": [ 512 | "Milaan Parmar is 96 years old. He lives in London, England.\n", 513 | "Ben Doe is 30 years old. He lives in Tampere, Finland.\n" 514 | ] 515 | } 516 | ], 517 | "source": [ 518 | "class Person:\n", 519 | " def __init__(self, firstname='Milaan', lastname='Parmar', age=96, country='England', city='London'):\n", 520 | " self.firstname = firstname\n", 521 | " self.lastname = lastname\n", 522 | " self.age = age\n", 523 | " self.country = country\n", 524 | " self.city = city\n", 525 | "\n", 526 | " def person_info(self):\n", 527 | " return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}.'\n", 528 | "\n", 529 | "p1 = Person()\n", 530 | "print(p1.person_info())\n", 531 | "p2 = Person('Ben', 'Doe', 30, 'Finland', 'Tampere')\n", 532 | "print(p2.person_info())" 533 | ] 534 | }, 535 | { 536 | "cell_type": "markdown", 537 | "metadata": {}, 538 | "source": [ 539 | "## Method to Modify Class Default Values\n", 540 | "\n", 541 | "In the example below, the **`Person`** class, all the constructor parameters have default values. In addition to that, we have skills parameter, which we can access using a method. Let us create **`add_skill`** method to add skills to the skills list." 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": 11, 547 | "metadata": {}, 548 | "outputs": [ 549 | { 550 | "name": "stdout", 551 | "output_type": "stream", 552 | "text": [ 553 | "Milaan Parmar is 96 years old. He lives in London, England.\n", 554 | "Ben Doe is 30 years old. He lives in Tampere, Finland.\n", 555 | "['Python', 'MATLAB', 'R']\n", 556 | "[]\n" 557 | ] 558 | } 559 | ], 560 | "source": [ 561 | "class Person:\n", 562 | " def __init__(self, firstname='Milaan', lastname='Parmar', age=96, country='England', city='London'):\n", 563 | " self.firstname = firstname\n", 564 | " self.lastname = lastname\n", 565 | " self.age = age\n", 566 | " self.country = country\n", 567 | " self.city = city\n", 568 | " self.skills = []\n", 569 | "\n", 570 | " def person_info(self):\n", 571 | " return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}.'\n", 572 | " def add_skill(self, skill):\n", 573 | " self.skills.append(skill)\n", 574 | "\n", 575 | "p1 = Person()\n", 576 | "print(p1.person_info())\n", 577 | "p1.add_skill('Python')\n", 578 | "p1.add_skill('MATLAB')\n", 579 | "p1.add_skill('R')\n", 580 | "p2 = Person('Ben', 'Doe', 30, 'Finland', 'Tampere')\n", 581 | "print(p2.person_info())\n", 582 | "print(p1.skills)\n", 583 | "print(p2.skills)" 584 | ] 585 | }, 586 | { 587 | "cell_type": "markdown", 588 | "metadata": {}, 589 | "source": [ 590 | "## Inheritance\n", 591 | "\n", 592 | "In Python, **[inheritance](https://github.com/milaan9/06_Python_Object_Class/blob/main/003_Python_Inheritance.ipynb)** is the process of inheriting the properties of the base class (or parent class) into a derived class (or child class).\n", 593 | "\n", 594 | "In an Object-oriented programming language, inheritance is an important aspect. Using inheritance we can reuse parent class code. Inheritance allows us to define a class that inherits all the methods and properties from parent class. The parent class or super or base class is the class which gives all the methods and properties. Child class is the class that inherits from another or parent class.\n", 595 | "\n", 596 | "In inheritance, the child class acquires and access all the data members, properties, and functions from the parent class. Also, a child class can also provide its specific implementation to the functions of the parent class.\n", 597 | "\n", 598 | "### Use of inheritance\n", 599 | "\n", 600 | "The main purpose of inheritance is the reusability of code because we can use the existing class to create a new class instead of creating it from scratch.\n", 601 | "\n", 602 | "**Syntax:**\n", 603 | "\n", 604 | "```python\n", 605 | "class BaseClass:\n", 606 | " Body of base class\n", 607 | "class DerivedClass(BaseClass):\n", 608 | " Body of derived class\n", 609 | "```" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": 12, 615 | "metadata": { 616 | "ExecuteTime": { 617 | "end_time": "2021-06-19T09:00:24.847816Z", 618 | "start_time": "2021-06-19T09:00:24.835121Z" 619 | } 620 | }, 621 | "outputs": [ 622 | { 623 | "name": "stdout", 624 | "output_type": "stream", 625 | "text": [ 626 | "This is Parent class\n", 627 | "This is Child class\n" 628 | ] 629 | } 630 | ], 631 | "source": [ 632 | "# Example 1: Use of Inheritance in Python\n", 633 | "\n", 634 | "class ClassOne: # Base class\n", 635 | " def func1(self):\n", 636 | " print('This is Parent class')\n", 637 | "\n", 638 | "class ClassTwo(ClassOne): # Derived class\n", 639 | " def func2(self):\n", 640 | " print('This is Child class')\n", 641 | "\n", 642 | "obj = ClassTwo()\n", 643 | "obj.func1()\n", 644 | "obj.func2()" 645 | ] 646 | }, 647 | { 648 | "cell_type": "markdown", 649 | "metadata": {}, 650 | "source": [ 651 | "Let us create a student class by inheriting from **`Person`** class." 652 | ] 653 | }, 654 | { 655 | "cell_type": "code", 656 | "execution_count": 13, 657 | "metadata": { 658 | "scrolled": true 659 | }, 660 | "outputs": [ 661 | { 662 | "name": "stdout", 663 | "output_type": "stream", 664 | "text": [ 665 | "Arthur Curry is 33 years old. He lives in London, England.\n", 666 | "['HTML', 'CSS', 'JavaScript']\n", 667 | "Emily Carter is 28 years old. He lives in Manchester, England.\n", 668 | "['Organizing', 'Marketing', 'Digital Marketing']\n" 669 | ] 670 | } 671 | ], 672 | "source": [ 673 | "# Example 2: Use of Inheritance in Python\n", 674 | "\n", 675 | "class Student(Person):\n", 676 | " pass\n", 677 | "\n", 678 | "s1 = Student('Arthur', 'Curry', 33, 'England', 'London')\n", 679 | "s2 = Student('Emily', 'Carter', 28, 'England', 'Manchester')\n", 680 | "print(s1.person_info())\n", 681 | "s1.add_skill('HTML')\n", 682 | "s1.add_skill('CSS')\n", 683 | "s1.add_skill('JavaScript')\n", 684 | "print(s1.skills)\n", 685 | "\n", 686 | "print(s2.person_info())\n", 687 | "s2.add_skill('Organizing')\n", 688 | "s2.add_skill('Marketing')\n", 689 | "s2.add_skill('Digital Marketing')\n", 690 | "print(s2.skills)" 691 | ] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": {}, 696 | "source": [ 697 | "**Exaplanation:**\n", 698 | "\n", 699 | "We did not call the **`__init__()`** constructor in the child class. If we didn't call it then we can still access all the properties from the parent. But if we do call the constructor we can access the parent properties by calling **`super()`**. \n", 700 | "We can add a new method to the child or we can override the parent class methods by creating the same method name in the child class. When we add the **`__init__()`** function, the child class will no longer inherit the parent's **`__init__()`** function." 701 | ] 702 | }, 703 | { 704 | "cell_type": "markdown", 705 | "metadata": {}, 706 | "source": [ 707 | "### Overriding parent method" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": 14, 713 | "metadata": {}, 714 | "outputs": [ 715 | { 716 | "name": "stdout", 717 | "output_type": "stream", 718 | "text": [ 719 | "Arthur Curry is 33 years old. He lives in London, England.\n", 720 | "['HTML', 'CSS', 'JavaScript']\n", 721 | "Emily Carter is 28 years old. She lives in Manchester, England.\n", 722 | "['Organizing', 'Marketing', 'Digital Marketing']\n" 723 | ] 724 | } 725 | ], 726 | "source": [ 727 | "# Example 2: Overriding parent method from above example\n", 728 | "\n", 729 | "class Student(Person):\n", 730 | " def __init__ (self, firstname='Milaan', lastname='Parmar',age=96, country='England', city='London', gender='male'):\n", 731 | " self.gender = gender\n", 732 | " super().__init__(firstname, lastname,age, country, city)\n", 733 | " \n", 734 | " def person_info(self):\n", 735 | " gender = 'He' if self.gender =='male' else 'She'\n", 736 | " return f'{self.firstname} {self.lastname} is {self.age} years old. {gender} lives in {self.city}, {self.country}.'\n", 737 | "\n", 738 | "s1 = Student('Arthur', 'Curry', 33, 'England', 'London','male')\n", 739 | "s2 = Student('Emily', 'Carter', 28, 'England', 'Manchester','female')\n", 740 | "print(s1.person_info())\n", 741 | "s1.add_skill('HTML')\n", 742 | "s1.add_skill('CSS')\n", 743 | "s1.add_skill('JavaScript')\n", 744 | "print(s1.skills)\n", 745 | "\n", 746 | "print(s2.person_info())\n", 747 | "s2.add_skill('Organizing')\n", 748 | "s2.add_skill('Marketing')\n", 749 | "s2.add_skill('Digital Marketing')\n", 750 | "print(s2.skills)" 751 | ] 752 | }, 753 | { 754 | "cell_type": "markdown", 755 | "metadata": {}, 756 | "source": [ 757 | "**Exaplanation**:\n", 758 | "\n", 759 | "We can use **`super()`** built-in function or the parent name Person to automatically inherit the methods and properties from its parent. In the example above we override the parent method. The child method has a different feature, it can identify, if the gender is male or female and assign the proper pronoun(He/She)." 760 | ] 761 | }, 762 | { 763 | "cell_type": "code", 764 | "execution_count": 15, 765 | "metadata": { 766 | "ExecuteTime": { 767 | "end_time": "2021-06-19T09:00:23.526541Z", 768 | "start_time": "2021-06-19T09:00:23.515798Z" 769 | } 770 | }, 771 | "outputs": [ 772 | { 773 | "name": "stdout", 774 | "output_type": "stream", 775 | "text": [ 776 | "Bird is ready\n", 777 | "Penguin is ready\n", 778 | "Penguin\n", 779 | "Swim faster\n", 780 | "Run faster\n" 781 | ] 782 | } 783 | ], 784 | "source": [ 785 | "# Example 3: Use of Inheritance in Python\n", 786 | "\n", 787 | "# parent class\n", 788 | "class Bird: \n", 789 | " def __init__(self):\n", 790 | " print(\"Bird is ready\")\n", 791 | "\n", 792 | " def whoisThis(self):\n", 793 | " print(\"Bird\")\n", 794 | "\n", 795 | " def swim(self):\n", 796 | " print(\"Swim faster\")\n", 797 | "\n", 798 | "# child class\n", 799 | "class Penguin(Bird):\n", 800 | " def __init__(self):\n", 801 | " # call super() function\n", 802 | " super().__init__()\n", 803 | " print(\"Penguin is ready\")\n", 804 | "\n", 805 | " def whoisThis(self):\n", 806 | " print(\"Penguin\")\n", 807 | "\n", 808 | " def run(self):\n", 809 | " print(\"Run faster\")\n", 810 | "\n", 811 | "peggy = Penguin()\n", 812 | "peggy.whoisThis()\n", 813 | "peggy.swim()\n", 814 | "peggy.run()" 815 | ] 816 | }, 817 | { 818 | "cell_type": "markdown", 819 | "metadata": {}, 820 | "source": [ 821 | "**Exaplanation**:\n", 822 | "\n", 823 | "In the above program, we created two classes i.e. **`Bird`** (parent class) and **`Penguin`** (child class). The child class inherits the functions of parent class. We can see this from the **`swim()`** method.\n", 824 | "\n", 825 | "Again, the child class modified the behavior of the parent class. We can see this from the **`whoisThis()`** method. Furthermore, we extend the functions of the parent class, by creating a new **`run()`** method.\n", 826 | "\n", 827 | "Additionally, we use the **`super()`** function inside the **`__init__()`** method. This allows us to run the **`__init__()`** method of the parent class inside the child class." 828 | ] 829 | }, 830 | { 831 | "cell_type": "markdown", 832 | "metadata": {}, 833 | "source": [ 834 | "## Encapsulation\n", 835 | "\n", 836 | "n Python, encapsulation is a method of wrapping data and functions into a single entity. For example, A class encapsulates all the data (methods and variables). Encapsulation means the internal representation of an object is generally hidden from outside of the object’s definition.\n", 837 | "\n", 838 | "
\n", 839 | "\n", 840 | "
\n", 841 | "\n", 842 | "Using OOP in Python, we can restrict access to methods and variables. This prevents data from direct modification which is called encapsulation. In Python, we denote private attributes using underscore as the prefix i.e single **`_`** or double **`__`**.\n", 843 | "\n", 844 | "### Need of Encapsulation\n", 845 | "\n", 846 | "Encapsulation acts as a protective layer. We can restrict access to methods and variables from outside, and It can prevent the data from being modified by accidental or unauthorized modification. Encapsulation provides security by hiding the data from the outside world.\n", 847 | "\n", 848 | "In Python, we do not have access modifiers directly, such as public, private, and protected. But we can achieve encapsulation by using single prefix underscore and double underscore to control access of variable and method within the Python program." 849 | ] 850 | }, 851 | { 852 | "cell_type": "code", 853 | "execution_count": 16, 854 | "metadata": { 855 | "ExecuteTime": { 856 | "end_time": "2021-06-19T09:00:15.687261Z", 857 | "start_time": "2021-06-19T09:00:15.666758Z" 858 | } 859 | }, 860 | "outputs": [ 861 | { 862 | "name": "stdout", 863 | "output_type": "stream", 864 | "text": [ 865 | "Selling Price: 900\n", 866 | "Selling Price: 900\n", 867 | "Selling Price: 1000\n" 868 | ] 869 | } 870 | ], 871 | "source": [ 872 | "# Example 1: Data Encapsulation in Python\n", 873 | "\n", 874 | "class Computer:\n", 875 | "\n", 876 | " def __init__(self):\n", 877 | " self.__maxprice = 900\n", 878 | "\n", 879 | " def sell(self):\n", 880 | " print(\"Selling Price: {}\".format(self.__maxprice))\n", 881 | "\n", 882 | " def setMaxPrice(self, price):\n", 883 | " self.__maxprice = price\n", 884 | "\n", 885 | "c = Computer()\n", 886 | "c.sell()\n", 887 | "\n", 888 | "# change the price\n", 889 | "c.__maxprice = 1000\n", 890 | "c.sell()\n", 891 | "\n", 892 | "# using setter function\n", 893 | "c.setMaxPrice(1000)\n", 894 | "c.sell()" 895 | ] 896 | }, 897 | { 898 | "cell_type": "markdown", 899 | "metadata": {}, 900 | "source": [ 901 | "**Explanation**:\n", 902 | "\n", 903 | "In the above program, we defined a **`Computer`** class.\n", 904 | "\n", 905 | "We used **`__init__()`** method to store the maximum selling price of **`Computer`**. We tried to modify the price. However, we can't change it because Python treats the **`__maxprice`** as private attributes.\n", 906 | "\n", 907 | "As shown, to change the value, we have to use a setter function i.e **`setMaxPrice()`** which takes price as a parameter." 908 | ] 909 | }, 910 | { 911 | "cell_type": "code", 912 | "execution_count": 17, 913 | "metadata": { 914 | "ExecuteTime": { 915 | "end_time": "2021-06-19T09:00:17.520737Z", 916 | "start_time": "2021-06-19T09:00:17.211663Z" 917 | }, 918 | "scrolled": false 919 | }, 920 | "outputs": [ 921 | { 922 | "ename": "AttributeError", 923 | "evalue": "'Employee' object has no attribute 'PrintName'", 924 | "output_type": "error", 925 | "traceback": [ 926 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 927 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 928 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;31m# Outside class\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[0mE\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mEmployee\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Bella\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m60000\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 13\u001b[1;33m \u001b[0mE\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mPrintName\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[0m\u001b[0;32m 14\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mE\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mE\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mPrintName\u001b[0m\u001b[1;33m(\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", 929 | "\u001b[1;31mAttributeError\u001b[0m: 'Employee' object has no attribute 'PrintName'" 930 | ] 931 | } 932 | ], 933 | "source": [ 934 | "# Example 2: Data Encapsulation in Python\n", 935 | "\n", 936 | "class Employee:\n", 937 | " def __init__(self, name, salary):\n", 938 | " self.name = name\n", 939 | " self.__salary = salary\n", 940 | "\n", 941 | " def show(self):\n", 942 | " print(\"Name is \", self.name, \"and salary is\", self.__salary)\n", 943 | "\n", 944 | "# Outside class\n", 945 | "E = Employee(\"Bella\", 60000)\n", 946 | "E.PrintName()\n", 947 | "print(E.name)\n", 948 | "print(E.PrintName())\n", 949 | "print(E.__salary)\n", 950 | "\n", 951 | "# AttributeError: 'Employee' object has no attribute '__salary'" 952 | ] 953 | }, 954 | { 955 | "cell_type": "markdown", 956 | "metadata": {}, 957 | "source": [ 958 | "**Explanation**:\n", 959 | "\n", 960 | "In the above example, we create a class called **`Employee`**. Within that class, we declare two variables **`name`** and **`__salary`**. We can observe that the name variable is accessible, but **`__salary`** is the **private variable**. We cannot access it from outside of class. If we try to access it, we will get an error." 961 | ] 962 | }, 963 | { 964 | "cell_type": "markdown", 965 | "metadata": {}, 966 | "source": [ 967 | "## Polymorphism\n", 968 | "\n", 969 | "Polymorphism is based on the greek words **Poly** (many) and **morphism** (forms). We will create a structure that can take or use many forms of objects.\n", 970 | "\n", 971 | "Polymorphism is an ability (in OOP) to use a common interface for multiple forms (data types).\n", 972 | "\n", 973 | "**Example 1:** The student can act as a student in college, act as a player on the ground, and as a daughter/brother in the home. \n", 974 | "\n", 975 | "**Example 2:** In the programming language, the **`+`** operator, acts as a concatenation and arithmetic addition.\n", 976 | "\n", 977 | "**Example 3:** If we need to color a shape, there are multiple shape options (rectangle, square, circle). However we could use the same method to color any shape. \n", 978 | "\n", 979 | "
\n", 980 | "\n", 981 | "
\n", 982 | "\n", 983 | "In Python, polymorphism allows us to define the child class methods with the same name as defined in the parent class." 984 | ] 985 | }, 986 | { 987 | "cell_type": "code", 988 | "execution_count": 18, 989 | "metadata": { 990 | "ExecuteTime": { 991 | "end_time": "2021-06-19T09:00:20.204307Z", 992 | "start_time": "2021-06-19T09:00:20.187708Z" 993 | } 994 | }, 995 | "outputs": [ 996 | { 997 | "name": "stdout", 998 | "output_type": "stream", 999 | "text": [ 1000 | "Parrot can fly\n", 1001 | "Penguin can't fly\n" 1002 | ] 1003 | } 1004 | ], 1005 | "source": [ 1006 | "# Example 1: Using Polymorphism in Python\n", 1007 | "\n", 1008 | "class Parrot:\n", 1009 | " def fly(self):\n", 1010 | " print(\"Parrot can fly\")\n", 1011 | " \n", 1012 | " def swim(self):\n", 1013 | " print(\"Parrot can't swim\")\n", 1014 | "\n", 1015 | "class Penguin:\n", 1016 | " def fly(self):\n", 1017 | " print(\"Penguin can't fly\")\n", 1018 | " \n", 1019 | " def swim(self):\n", 1020 | " print(\"Penguin can swim\")\n", 1021 | "\n", 1022 | "# common interface\n", 1023 | "def flying_test(bird):\n", 1024 | " bird.fly()\n", 1025 | "\n", 1026 | "#instantiate objects\n", 1027 | "blu = Parrot()\n", 1028 | "peggy = Penguin()\n", 1029 | "\n", 1030 | "# passing the object\n", 1031 | "flying_test(blu)\n", 1032 | "flying_test(peggy)" 1033 | ] 1034 | }, 1035 | { 1036 | "cell_type": "markdown", 1037 | "metadata": {}, 1038 | "source": [ 1039 | "**Exaplanation**:\n", 1040 | "\n", 1041 | "In the above program, we defined two classes **`Parrot`** and **`Penguin`**. Each of them have a common **`fly()`** method. However, their functions are different.\n", 1042 | "\n", 1043 | "To use polymorphism, we created a common interface i.e **`flying_test()`** function that takes any object and calls the object's **`fly()`** method. Thus, when we passed the **`blu`** and **`peggy`** objects in the **`flying_test()`** function, it ran effectively." 1044 | ] 1045 | }, 1046 | { 1047 | "cell_type": "code", 1048 | "execution_count": 19, 1049 | "metadata": { 1050 | "ExecuteTime": { 1051 | "end_time": "2021-06-19T09:00:21.662302Z", 1052 | "start_time": "2021-06-19T09:00:21.639840Z" 1053 | }, 1054 | "scrolled": true 1055 | }, 1056 | "outputs": [ 1057 | { 1058 | "name": "stdout", 1059 | "output_type": "stream", 1060 | "text": [ 1061 | "Area of circle: 254.34\n", 1062 | "Area of Rectangle: 54\n" 1063 | ] 1064 | } 1065 | ], 1066 | "source": [ 1067 | "# Example 2: Using Polymorphism in Python\n", 1068 | "\n", 1069 | "class Circle:\n", 1070 | " pi = 3.14\n", 1071 | "\n", 1072 | " def __init__(self, redius):\n", 1073 | " self.radius = redius\n", 1074 | "\n", 1075 | " def calculate_area(self):\n", 1076 | " print(\"Area of circle:\", self.pi * self.radius * self.radius)\n", 1077 | "\n", 1078 | "class Rectangle:\n", 1079 | " def __init__(self, length, width):\n", 1080 | " self.length = length\n", 1081 | " self.width = width\n", 1082 | "\n", 1083 | " def calculate_area(self):\n", 1084 | " print(\"Area of Rectangle:\", self.length * self.width)\n", 1085 | "\n", 1086 | "cir = Circle(9)\n", 1087 | "rect = Rectangle(9, 6)\n", 1088 | "cir.calculate_area() # Output Area of circle: 254.34\n", 1089 | "\n", 1090 | "rect.calculate_area() # Output Area od Rectangle: 54" 1091 | ] 1092 | }, 1093 | { 1094 | "cell_type": "markdown", 1095 | "metadata": {}, 1096 | "source": [ 1097 | "**Exaplanation**:\n", 1098 | "\n", 1099 | "In the above example, we created two classes called **`Circle`** and **`Rectangle`**. In both classes, we created the same method with the name **`calculate_area`**. This method acts differently in both classes. In the case of the **`Circle`** class, it calculates the area of the circle, whereas, in the case of a **`Rectangle`** class, it calculates the area of a rectangle." 1100 | ] 1101 | }, 1102 | { 1103 | "cell_type": "markdown", 1104 | "metadata": {}, 1105 | "source": [ 1106 | "## Key Points to Remember:\n", 1107 | "* Object-Oriented Programming makes the program easy to understand as well as efficient.\n", 1108 | "* Since the class is sharable, the code can be reused.\n", 1109 | "* Data is safe and secure with data abstraction.\n", 1110 | "* Polymorphism allows the same interface for different objects, so programmers can write efficient code." 1111 | ] 1112 | }, 1113 | { 1114 | "cell_type": "markdown", 1115 | "metadata": {}, 1116 | "source": [ 1117 | "## 💻 Exercises ➞ Objects and Classes\n", 1118 | "\n", 1119 | "### Exercises ➞ Level 1\n", 1120 | "\n", 1121 | "1. Python has the module called **`statistics`** and we can use this module to do all the statistical calculations. However, to learn how to make function and reuse function let us try to develop a program, which calculates the measure of central tendency of a sample (**`mean`**, **`median`**, **`mode`**) and measure of variability (**`range`**, **`variance`**, **`standard deviation`**). In addition to those measures, find the **`min`**, **`max`**, **`count`**, **`percentile`**, and **`frequency distribution`** of the sample. You can create a class called **`statistics`** and create all the functions that do statistical calculations as methods for the **`statistics`** class. Check the output below.\n", 1122 | "\n", 1123 | " - ```python\n", 1124 | "ages = [31, 26, 34, 37, 27, 26, 32, 32, 26, 27, 27, 24, 32, 33, 27, 25, 26, 38, 37, 31, 34, 24, 33, 29, 26]\n", 1125 | "print('Count:', data.count()) # 25\n", 1126 | "print('Sum: ', data.sum()) # 744\n", 1127 | "print('Min: ', data.min()) # 24\n", 1128 | "print('Max: ', data.max()) # 38\n", 1129 | "print('Range: ', data.range() # 14\n", 1130 | "print('Mean: ', data.mean()) # 30\n", 1131 | "print('Median: ', data.median()) # 29\n", 1132 | "print('Mode: ', data.mode()) # {'mode': 26, 'count': 5}\n", 1133 | "print('Standard Deviation: ', data.std()) # 4.2\n", 1134 | "print('Variance: ', data.var()) # 17.5\n", 1135 | "print('Frequency Distribution: ', data.freq_dist()) # [(20.0, 26), (16.0, 27), (12.0, 32), (8.0, 37), (8.0, 34), (8.0, 33), (8.0, 31), (8.0, 24), (4.0, 38), (4.0, 29), (4.0, 25)]\n", 1136 | " ```\n", 1137 | "\n", 1138 | " -```sh\n", 1139 | "# you output should look like this:\n", 1140 | "print(data.describe())\n", 1141 | "Count: 25\n", 1142 | "Sum: 744\n", 1143 | "Min: 24\n", 1144 | "Max: 38\n", 1145 | "Range: 14\n", 1146 | "Mean: 30\n", 1147 | "Median: 29\n", 1148 | "Mode: (26, 5)\n", 1149 | "Variance: 17.5\n", 1150 | "Standard Deviation: 4.2\n", 1151 | "Frequency Distribution: [(20.0, 26), (16.0, 27), (12.0, 32), (8.0, 37), (8.0, 34), (8.0, 33), (8.0, 31), (8.0, 24), (4.0, 38), (4.0, 29), (4.0, 25)]\n", 1152 | " ``` \n", 1153 | "\n", 1154 | "\n", 1155 | "### Exercises ➞ Level 2\n", 1156 | "\n", 1157 | "1. Create a class called **`PersonAccount`**. It has **`firstname`**, **`lastname`**, **`incomes`**, **`expenses`** properties and it has **`total_income`**, **`total_expense`**, **`account_info`**, **`add_income`**, **`add_expense`** and **`account_balance`** methods. Incomes is a set of incomes and its description. The same goes for expenses." 1158 | ] 1159 | }, 1160 | { 1161 | "cell_type": "code", 1162 | "execution_count": null, 1163 | "metadata": {}, 1164 | "outputs": [], 1165 | "source": [] 1166 | } 1167 | ], 1168 | "metadata": { 1169 | "hide_input": false, 1170 | "kernelspec": { 1171 | "display_name": "Python 3", 1172 | "language": "python", 1173 | "name": "python3" 1174 | }, 1175 | "language_info": { 1176 | "codemirror_mode": { 1177 | "name": "ipython", 1178 | "version": 3 1179 | }, 1180 | "file_extension": ".py", 1181 | "mimetype": "text/x-python", 1182 | "name": "python", 1183 | "nbconvert_exporter": "python", 1184 | "pygments_lexer": "ipython3", 1185 | "version": "3.8.8" 1186 | }, 1187 | "toc": { 1188 | "base_numbering": 1, 1189 | "nav_menu": {}, 1190 | "number_sections": true, 1191 | "sideBar": true, 1192 | "skip_h1_title": false, 1193 | "title_cell": "Table of Contents", 1194 | "title_sidebar": "Contents", 1195 | "toc_cell": false, 1196 | "toc_position": {}, 1197 | "toc_section_display": true, 1198 | "toc_window_display": false 1199 | }, 1200 | "varInspector": { 1201 | "cols": { 1202 | "lenName": 16, 1203 | "lenType": 16, 1204 | "lenVar": 40 1205 | }, 1206 | "kernels_config": { 1207 | "python": { 1208 | "delete_cmd_postfix": "", 1209 | "delete_cmd_prefix": "del ", 1210 | "library": "var_list.py", 1211 | "varRefreshCmd": "print(var_dic_list())" 1212 | }, 1213 | "r": { 1214 | "delete_cmd_postfix": ") ", 1215 | "delete_cmd_prefix": "rm(", 1216 | "library": "var_list.r", 1217 | "varRefreshCmd": "cat(var_dic_list()) " 1218 | } 1219 | }, 1220 | "types_to_exclude": [ 1221 | "module", 1222 | "function", 1223 | "builtin_function_or_method", 1224 | "instance", 1225 | "_Feature" 1226 | ], 1227 | "window_display": false 1228 | } 1229 | }, 1230 | "nbformat": 4, 1231 | "nbformat_minor": 2 1232 | } 1233 | -------------------------------------------------------------------------------- /002_Python_Classes_and_Objects.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 | "# Python Classes and Objects\n", 17 | "\n", 18 | "In this class, you will learn about the core functionality of Python objects and classes. You'll learn what a class is, how to create it and use it in your program." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Python Classes and Objects\n", 26 | "\n", 27 | "Python is an object oriented programming language. Everything in Python is an object, with its properties and methods. A number, string, list, dictionary, tuple, set etc. used in a program is an object of a corresponding built-in class. We create class to create an object. Unlike procedure oriented programming, where the main emphasis is on functions, object oriented programming stresses on objects.\n", 28 | "\n", 29 | "An object is simply a collection of data (variables) and methods (functions) that act on those data. Similarly, a class is a blueprint for that object.\n", 30 | "\n", 31 | "We can think of class as a sketch (prototype) of a house. It contains all the details about the floors, doors, windows etc. Based on these descriptions we build the house. House is the object.\n", 32 | "\n", 33 | "As many houses can be made from a house's blueprint, we can create many objects from a class. An object is also called an instance of a class and the process of creating this object is called **instantiation**. We instantiate a class to create an object. The class defines attributes and the behavior of the object, while the object, on the other hand, represents the class.\n", 34 | "\n", 35 | "We have been working with classes and objects right from the beginning of this challenge unknowingly. Every element in a Python program is an object of a class. Let us check if everything in python is a class:" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 1, 41 | "metadata": {}, 42 | "outputs": [ 43 | { 44 | "name": "stdout", 45 | "output_type": "stream", 46 | "text": [ 47 | "➡ 10 belongs to \n", 48 | "➡ 'string' belongs to \n", 49 | "➡ True belongs to \n", 50 | "➡ [1,2,3] belongs to \n", 51 | "➡ (1,2,3) belongs to \n", 52 | "➡ {1,2,3} belongs to \n", 53 | "➡ {1:'A', 2:'B'} belongs to \n" 54 | ] 55 | } 56 | ], 57 | "source": [ 58 | "num = 10\n", 59 | "print(\"➡ 10 belongs to\",type(num)) # \n", 60 | "\n", 61 | "string = 'string'\n", 62 | "print(\"➡ 'string' belongs to\",type(string)) # \n", 63 | "\n", 64 | "boolean = True\n", 65 | "print(\"➡ True belongs to\",type(boolean)) # \n", 66 | "\n", 67 | "list_1 = [1,2,3]\n", 68 | "print(\"➡ [1,2,3] belongs to\",type(list_1)) # \n", 69 | "\n", 70 | "tuple_1 = (1,2,3)\n", 71 | "print(\"➡ (1,2,3) belongs to\",type(tuple_1)) # \n", 72 | "\n", 73 | "set_1 = {1,2,3}\n", 74 | "print(\"➡ {1,2,3} belongs to\",type(set_1)) # \n", 75 | " \n", 76 | "dict_1 = {1:'A', 2:'B'}\n", 77 | "print(\"➡ {1:'A', 2:'B'} belongs to\",type(dict_1)) # " 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "## Defining a Class in Python\n", 85 | "\n", 86 | "Like function definitions begin with the **[def](https://github.com/milaan9/01_Python_Introduction/blob/main/Python_Keywords_List.ipynb)** keyword in Python, class definitions begin with a **[class](https://github.com/milaan9/01_Python_Introduction/blob/main/Python_Keywords_List.ipynb)** keyword.\n", 87 | "\n", 88 | "The first string inside the class is called docstring and has a brief description about the class. Although not mandatory, this is highly recommended.\n", 89 | "\n", 90 | "Here is a simple class definition.\n", 91 | "\n", 92 | "```python\n", 93 | "class MyNewClass:\n", 94 | " '''This is a docstring. I have created a new class'''\n", 95 | " pass\n", 96 | "```\n", 97 | "\n", 98 | "A class creates a new local **[namespace](https://github.com/milaan9/01_Python_Introduction/blob/main/013_Python_Namespace_and_Scope.ipynb)** where all its attributes are defined. Attributes may be data or functions.\n", 99 | "\n", 100 | "There are also special attributes in it that begins with double underscores **`__`**. For example, **`__doc__`** gives us the docstring of that class.\n", 101 | "\n", 102 | "As soon as we define a class, a new class object is created with the same name. This class object allows us to access the different attributes as well as to instantiate new objects of that class." 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 2, 108 | "metadata": { 109 | "ExecuteTime": { 110 | "end_time": "2021-06-20T19:09:50.823644Z", 111 | "start_time": "2021-06-20T19:09:50.811928Z" 112 | } 113 | }, 114 | "outputs": [ 115 | { 116 | "name": "stdout", 117 | "output_type": "stream", 118 | "text": [ 119 | "10\n", 120 | "\n", 121 | "This is a person class\n" 122 | ] 123 | } 124 | ], 125 | "source": [ 126 | "# Example 1: Creating Class and Object in Python\n", 127 | "\n", 128 | "class Person:\n", 129 | " \"This is a person class\"\n", 130 | " age = 10\n", 131 | "\n", 132 | " def greet(self):\n", 133 | " print('Hello')\n", 134 | "\n", 135 | "print(Person.age) # Output: 10\n", 136 | "\n", 137 | "print(Person.greet) # Output: \n", 138 | "\n", 139 | "print(Person.__doc__) # Output: 'This is my second class'" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "## Creating an Object in Python\n", 147 | "\n", 148 | "We saw that the class object could be used to access different attributes.\n", 149 | "\n", 150 | "It can also be used to create new object instances (instantiation) of that class. The procedure to create an object is similar to a [**function**](https://github.com/milaan9/04_Python_Functions/blob/main/001_Python_Functions.ipynb) call.\n", 151 | "\n", 152 | "```python\n", 153 | "harry = Person()\n", 154 | "```\n", 155 | "This will create a new object instance named **`harry`**. We can access the attributes of objects using the object name prefix.\n", 156 | "\n", 157 | "Attributes may be data or method. Methods of an object are corresponding functions of that class.\n", 158 | "\n", 159 | "This means to say, since **`Person.greet`** is a function object (attribute of class), **`Person.greet`** will be a method object." 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 3, 165 | "metadata": { 166 | "ExecuteTime": { 167 | "end_time": "2021-06-20T19:09:52.160548Z", 168 | "start_time": "2021-06-20T19:09:52.140040Z" 169 | } 170 | }, 171 | "outputs": [ 172 | { 173 | "name": "stdout", 174 | "output_type": "stream", 175 | "text": [ 176 | "\n", 177 | ">\n", 178 | "Hello\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "class Person:\n", 184 | " \"This is a person class\"\n", 185 | " age = 10\n", 186 | "\n", 187 | " def greet(self):\n", 188 | " print('Hello')\n", 189 | "\n", 190 | "# create a new object of Person class\n", 191 | "harry = Person()\n", 192 | "\n", 193 | "print(Person.greet) # Output: \n", 194 | "\n", 195 | "print(harry.greet) # Output: >\n", 196 | "\n", 197 | "# Calling object's greet() method\n", 198 | "harry.greet() # Output: Hello" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "**Explanation:**\n", 206 | "\n", 207 | "You may have noticed the **`self`** parameter in function definition inside the class but we called the method simply as **`harry.greet()`** without any [**arguments**](https://github.com/milaan9/04_Python_Functions/blob/main/004_Python_Function_Arguments.ipynb). It still worked.\n", 208 | "\n", 209 | "This is because, whenever an object calls its method, the object itself is passed as the first argument. So, **`harry.greet()`** translates into **`Person.greet(harry)`**.\n", 210 | "\n", 211 | "In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method's object before the first argument.\n", 212 | "\n", 213 | "For these reasons, the first argument of the function in class must be the object itself. This is conventionally called self. It can be named otherwise but we highly recommend to follow the convention.\n", 214 | "\n", 215 | "Now you must be familiar with class object, instance object, function object, method object and their differences." 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## Constructors in Python\n", 223 | "\n", 224 | "In Python, if a class functions that begin with double underscore **`__`** are called special functions as they have special meaning.\n", 225 | "\n", 226 | "A class without a constructor is not really useful in real applications. A constructor is a special type of method used in Python to initialize the object of a Class. The constructor will be executed automatically when the object is created. If we create three objects, the constructor is called three times and initialize each object.\n", 227 | "\n", 228 | "The main purpose of the constructor is to declare and initialize instance variables. It can take at least one argument that is **`self`**. The **`__init__()`** method is called the constructor in Python. In other words, the name of the constructor should be **`__init__(self)`**.\n", 229 | "\n", 230 | "**A constructor is optional, and if we do not provide any constructor, then Python provides the default constructor. Every class in Python has a constructor, but it’s not required to define it.**" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 4, 236 | "metadata": { 237 | "ExecuteTime": { 238 | "end_time": "2021-06-20T19:09:53.713272Z", 239 | "start_time": "2021-06-20T19:09:53.699603Z" 240 | } 241 | }, 242 | "outputs": [ 243 | { 244 | "name": "stdout", 245 | "output_type": "stream", 246 | "text": [ 247 | "5+6j\n" 248 | ] 249 | } 250 | ], 251 | "source": [ 252 | "# Example 1:\n", 253 | "\n", 254 | "class ComplexNumber:\n", 255 | " def __init__(self,r=0,i=1):\n", 256 | " self.real=r;\n", 257 | " self.imag=i;\n", 258 | "\n", 259 | " def getData(self):\n", 260 | " print('{0}+{1}j'.format(self.real,self.imag))\n", 261 | "\n", 262 | "\n", 263 | "c1=ComplexNumber(5,6)\n", 264 | "c1.getData()" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 5, 270 | "metadata": { 271 | "ExecuteTime": { 272 | "end_time": "2021-06-20T19:09:54.928101Z", 273 | "start_time": "2021-06-20T19:09:54.603889Z" 274 | } 275 | }, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "2+3j\n", 282 | "(5, 0, 10)\n" 283 | ] 284 | }, 285 | { 286 | "ename": "AttributeError", 287 | "evalue": "'ComplexNumber' object has no attribute 'attr'", 288 | "output_type": "error", 289 | "traceback": [ 290 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 291 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 292 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 23\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 24\u001b[0m \u001b[1;31m# but c1 object doesn't have attribute 'attr'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 25\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnum1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mattr\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# AttributeError: 'ComplexNumber' object has no attribute 'attr'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 293 | "\u001b[1;31mAttributeError\u001b[0m: 'ComplexNumber' object has no attribute 'attr'" 294 | ] 295 | } 296 | ], 297 | "source": [ 298 | "# Example 2:\n", 299 | "\n", 300 | "class ComplexNumber:\n", 301 | " def __init__(self, r=0, i=0):\n", 302 | " self.real = r\n", 303 | " self.imag = i\n", 304 | "\n", 305 | " def get_data(self):\n", 306 | " print(f'{self.real}+{self.imag}j')\n", 307 | "\n", 308 | "# Create a new ComplexNumber object\n", 309 | "num1 = ComplexNumber(2, 3)\n", 310 | "\n", 311 | "# Call get_data() method\n", 312 | "\n", 313 | "num1.get_data() # Output: 2+3j\n", 314 | "\n", 315 | "# Create another ComplexNumber object and create a new attribute 'attr'\n", 316 | "num2 = ComplexNumber(5)\n", 317 | "num2.attr = 10\n", 318 | "\n", 319 | "print((num2.real, num2.imag, num2.attr)) # Output: (5, 0, 10)\n", 320 | "\n", 321 | "# but c1 object doesn't have attribute 'attr'\n", 322 | "print(num1.attr) # AttributeError: 'ComplexNumber' object has no attribute 'attr'" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "**Explanation:**\n", 330 | "\n", 331 | "In the above example, we defined a new class to represent complex numbers. It has two functions, **`__init__()`** to initialize the variables (defaults to zero) and **`get_data()`** to display the number properly.\n", 332 | "\n", 333 | "An interesting thing to note in the above step is that attributes of an object can be created on the fly. We created a new attribute **`attr`** for object **`num2`** and read it as well. But this does not create that attribute for object **`num1`**." 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": {}, 339 | "source": [ 340 | "## Types of Constructors\n", 341 | "\n", 342 | "We have two types of constructors in Python:\n", 343 | "\n", 344 | "* **Non-parameterized:** The constructors in Python which have no parameter is known as a non parameterized constructor. The non-parameterized constructor uses when we do not want to manipulate the value or the constructor that has only self as an argument.\n", 345 | "* **Parameterized Constructor:** The constructor with parameters is known as a parameterized constructor. The parameterized constructor has multiple parameters along with the self." 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 6, 351 | "metadata": { 352 | "ExecuteTime": { 353 | "end_time": "2021-06-20T19:09:57.889505Z", 354 | "start_time": "2021-06-20T19:09:57.874860Z" 355 | } 356 | }, 357 | "outputs": [ 358 | { 359 | "name": "stdout", 360 | "output_type": "stream", 361 | "text": [ 362 | "This is non parametrized constructor\n", 363 | "Hello Arthur\n" 364 | ] 365 | } 366 | ], 367 | "source": [ 368 | "# Example 1: Constructor without parameters (Non-parameterized)\n", 369 | "\n", 370 | "class Test:\n", 371 | " # Constructor - non parameterized\n", 372 | " def __init__(self):\n", 373 | " print(\"This is non parametrized constructor\")\n", 374 | "\n", 375 | " def show(self, name):\n", 376 | " print(\"Hello\", name)\n", 377 | "\n", 378 | "# creating object of the class\n", 379 | "t = Test() # Output:This is non parametrized constructor\n", 380 | "\n", 381 | "# calling the instance method\n", 382 | "t.show(\"Arthur\") # Output Hello Arthur" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 7, 388 | "metadata": { 389 | "ExecuteTime": { 390 | "end_time": "2021-06-20T19:09:59.101408Z", 391 | "start_time": "2021-06-20T19:09:59.082857Z" 392 | }, 393 | "scrolled": true 394 | }, 395 | "outputs": [ 396 | { 397 | "name": "stdout", 398 | "output_type": "stream", 399 | "text": [ 400 | "This is parametrized constructor\n", 401 | "Fruit is Apple and Color is red\n" 402 | ] 403 | } 404 | ], 405 | "source": [ 406 | "# Example 2: Constructor with parameters\n", 407 | "\n", 408 | "class Fruit:\n", 409 | " # parameterized constructor\n", 410 | " def __init__(self, name, color):\n", 411 | " print(\"This is parametrized constructor\")\n", 412 | " self.name = name\n", 413 | " self.color = color\n", 414 | "\n", 415 | " def show(self):\n", 416 | " print(\"Fruit is\", self.name, \"and Color is\", self.color)\n", 417 | "\n", 418 | "# creating object of the class\n", 419 | "# this will invoke parameterized constructor\n", 420 | "obj = Fruit(\"Apple\", \"red\") # Output This is parametrized constructor\n", 421 | "\n", 422 | "# calling the instance method using the object\n", 423 | "obj.show() # Output Fruit is Apple and Color is red" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "**Explanation:** \n", 431 | "\n", 432 | "In the above example, we create a **parameterized constructor** with parameters **`name`** and **`color`**. When we create an object of **`Test`** class called **`obj`**, the parameterized constructor will be executed automatically." 433 | ] 434 | }, 435 | { 436 | "cell_type": "code", 437 | "execution_count": 8, 438 | "metadata": { 439 | "ExecuteTime": { 440 | "end_time": "2021-06-20T19:10:00.225422Z", 441 | "start_time": "2021-06-20T19:10:00.204916Z" 442 | }, 443 | "scrolled": true 444 | }, 445 | "outputs": [ 446 | { 447 | "name": "stdout", 448 | "output_type": "stream", 449 | "text": [ 450 | "This is parametrized constructor\n", 451 | "Hello World\n" 452 | ] 453 | } 454 | ], 455 | "source": [ 456 | "# Example 3:\n", 457 | "\n", 458 | "class Student:\n", 459 | " # Constructor - parameterized\n", 460 | " def __init__(self, name):\n", 461 | " print(\"This is parametrized constructor\")\n", 462 | " self.name = name\n", 463 | "\n", 464 | " def show(self):\n", 465 | " print(\"Hello\",self.name)\n", 466 | "\n", 467 | "student = Student(\"World\")\n", 468 | "student.show()" 469 | ] 470 | }, 471 | { 472 | "cell_type": "code", 473 | "execution_count": 9, 474 | "metadata": { 475 | "ExecuteTime": { 476 | "end_time": "2021-06-20T19:10:00.807450Z", 477 | "start_time": "2021-06-20T19:10:00.791827Z" 478 | } 479 | }, 480 | "outputs": [ 481 | { 482 | "name": "stdout", 483 | "output_type": "stream", 484 | "text": [ 485 | "Jane is a students\n", 486 | "Bella is also a students\n" 487 | ] 488 | } 489 | ], 490 | "source": [ 491 | "# Example 4: Creating Class and Object in Python\n", 492 | "\n", 493 | "class MasterStudentClass:\n", 494 | " # class attribute\n", 495 | " species = \"students\"\n", 496 | "\n", 497 | " # instance attribute\n", 498 | " def __init__(self, name, age):\n", 499 | " self.name = name\n", 500 | " self.age = age\n", 501 | "\n", 502 | "# instantiate the Parrot class\n", 503 | "jane = MasterStudentClass(\"Jane\", 18)\n", 504 | "bella = MasterStudentClass(\"Bella\", 19)\n", 505 | "candy = MasterStudentClass(\"Candy\", 17)\n", 506 | "lucia = MasterStudentClass(\"Lucia\", 18)\n", 507 | "ran = MasterStudentClass(\"Ran\", 20)\n", 508 | "\n", 509 | "# access the class attributes\n", 510 | "print(\"Jane is a {}\".format(jane.__class__.species)) # Jane is a students\n", 511 | "print(\"Bella is also a {}\".format(bella.__class__.species)) # Bella is also a students" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 10, 517 | "metadata": { 518 | "ExecuteTime": { 519 | "end_time": "2021-06-20T19:10:01.192212Z", 520 | "start_time": "2021-06-20T19:10:01.180495Z" 521 | } 522 | }, 523 | "outputs": [ 524 | { 525 | "name": "stdout", 526 | "output_type": "stream", 527 | "text": [ 528 | "Name : Mark , Id : 101\n" 529 | ] 530 | } 531 | ], 532 | "source": [ 533 | "# Example 5:\n", 534 | "\n", 535 | "class Student:\n", 536 | " \n", 537 | " # class attribute\n", 538 | " 'Common base class for all students'\n", 539 | " student_count=0\n", 540 | "\n", 541 | " def __init__(self, name, id): # check the number of underscore '_' used\n", 542 | " self.name = name\n", 543 | " self.id = id\n", 544 | " Student.student_count+=1\n", 545 | "\n", 546 | " def printStudentData(self):\n", 547 | " print (\"Name : \", self.name, \", Id : \", self.id)\n", 548 | "\n", 549 | "s=Student(\"Mark\",101)\n", 550 | "s.printStudentData() # Name : Mark , Id : 101" 551 | ] 552 | }, 553 | { 554 | "cell_type": "markdown", 555 | "metadata": {}, 556 | "source": [ 557 | "**Explanation:**\n", 558 | "\n", 559 | "* The variable **`student_count`** is a class variable whose value is shared among all the instances of a in this class. This can be accessed as **`Student.student_count`** from inside the class or outside the class.\n", 560 | "\n", 561 | "* The first method **`__init__()`** is a special method, which is called class **constructor** or **initialization** method that Python calls when you create a new instance of this class.\n", 562 | "\n", 563 | "* You declare other class methods like normal functions with the exception that the first argument to each method is self. Python adds the self argument to the list for you; you do not need to include it when you call the methods." 564 | ] 565 | }, 566 | { 567 | "cell_type": "markdown", 568 | "metadata": {}, 569 | "source": [ 570 | "## Creating Instance Objects\n", 571 | "\n", 572 | "To create instances of a class, you call the class using class name and pass in whatever arguments its **`__init__`** method accepts. \n", 573 | "Lets Create Studnet class object of above example : \n", 574 | "\n", 575 | "```python\n", 576 | "std=Student('Vijay','102')\n", 577 | "```" 578 | ] 579 | }, 580 | { 581 | "cell_type": "markdown", 582 | "metadata": {}, 583 | "source": [ 584 | "## Accessing Attributes with `self` Parameter\n", 585 | "\n", 586 | "The **`self`** is used to represent the instance of the class. It is the default variable that is always pointing to the current object.\n", 587 | "\n", 588 | "By using **`self`**, we can access the instance variable and instance method of the object. While defining constructor and instance method, the **`self`** is their first parameter.\n", 589 | "\n", 590 | "It’s not required the first parameter named to be **`self`**, we can give any name whatever we like, but it has to be the first parameter of any function in the class.\n", 591 | "\n", 592 | "You access the object's attributes using the dot operator with object. Class variable would be accessed using class name as follows:" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "execution_count": 11, 598 | "metadata": { 599 | "ExecuteTime": { 600 | "end_time": "2021-06-20T19:10:03.478324Z", 601 | "start_time": "2021-06-20T19:10:03.459773Z" 602 | } 603 | }, 604 | "outputs": [ 605 | { 606 | "name": "stdout", 607 | "output_type": "stream", 608 | "text": [ 609 | "Employee ID is 19116 and name is Amy\n", 610 | "Employee of IT department\n" 611 | ] 612 | } 613 | ], 614 | "source": [ 615 | "# Example 1:\n", 616 | "\n", 617 | "class Employee:\n", 618 | " def __init__(self, id, name):\n", 619 | " # instance variable\n", 620 | " self.id = id\n", 621 | " self.name = name\n", 622 | " \n", 623 | " # instance method\n", 624 | " def info(self):\n", 625 | " print(\"Employee ID is \", self.id, \"and name is\", self.name)\n", 626 | "\n", 627 | " # instance method \n", 628 | " def department(self):\n", 629 | " print(\"Employee of IT department\")\n", 630 | "\n", 631 | "emp = Employee(19116, \"Amy\", )\n", 632 | "emp.info() # Output Employee ID is 19116 and name is Amy\n", 633 | "\n", 634 | "emp.department() # Output Employee of IT department" 635 | ] 636 | }, 637 | { 638 | "cell_type": "markdown", 639 | "metadata": {}, 640 | "source": [ 641 | "**Explanation:** \n", 642 | "\n", 643 | "In the above example, all methods including **`__init__`** have a self parameter. We created two instance variables **`id`** and **`name`**. Then we create an object of a class **`Employee`** called **`emp`** and accessed instance methods of the class called **`info()`** and **`department()`** respectively." 644 | ] 645 | }, 646 | { 647 | "cell_type": "code", 648 | "execution_count": 12, 649 | "metadata": { 650 | "ExecuteTime": { 651 | "end_time": "2021-06-20T19:10:04.386517Z", 652 | "start_time": "2021-06-20T19:10:04.363084Z" 653 | } 654 | }, 655 | "outputs": [ 656 | { 657 | "name": "stdout", 658 | "output_type": "stream", 659 | "text": [ 660 | "Total Student : 3\n", 661 | "Name : Milan , Id : 101\n", 662 | "Name : Vijay , Id : 102\n", 663 | "Name : Chirag , Id : 103\n" 664 | ] 665 | } 666 | ], 667 | "source": [ 668 | "# Example 2:\n", 669 | "\n", 670 | "class Student:\n", 671 | " 'Common base class for all students'\n", 672 | " student_count=0\n", 673 | "\n", 674 | " def __init__(self, name, id):\n", 675 | " self.name = name\n", 676 | " self.id = id\n", 677 | " Student.student_count+=1\n", 678 | "\n", 679 | " def printStudentData(self):\n", 680 | " print (\"Name : \", self.name, \", Id : \", self.id)\n", 681 | "\n", 682 | "std1=Student(\"Milan\",101)\n", 683 | "std2=Student(\"Vijay\",102)\n", 684 | "std3=Student(\"Chirag\",103)\n", 685 | "\n", 686 | "print(\"Total Student : \",Student.student_count)\n", 687 | "std1.printStudentData()\n", 688 | "std2.printStudentData()\n", 689 | "std3.printStudentData()" 690 | ] 691 | }, 692 | { 693 | "cell_type": "markdown", 694 | "metadata": {}, 695 | "source": [ 696 | "Instead of using the normal statements to access attributes, you can use the following functions:\n", 697 | "\n", 698 | "* **`getattr(obj, name[, default])`** − to access the attribute of object. \n", 699 | "\n", 700 | "* **`hasattr(obj,name)`** − to check if an attribute exists or not. \n", 701 | "\n", 702 | "* **`setattr(obj,name,value)`** − to set an attribute. If attribute does not exist, then it would be created. \n", 703 | "\n", 704 | "* **`delattr(obj, name)`** − to delete an attribute. \n", 705 | "\n", 706 | "**Example:**\n", 707 | "\n", 708 | "```python\n", 709 | "hasattr(std1, 'id') # Returns true if 'id' attribute exists\n", 710 | "getattr(std1, 'id') # Returns value of 'id' attribute\n", 711 | "setattr(std1, 'id', 104) # Set attribute 'id' 104\n", 712 | "delattr(std1, 'id') # Delete attribute 'id'\n", 713 | "```" 714 | ] 715 | }, 716 | { 717 | "cell_type": "markdown", 718 | "metadata": {}, 719 | "source": [ 720 | "## Built-In Class Attributes\n", 721 | "\n", 722 | "Every Python class keeps following built-in attributes and they can be accessed using **dot operator** like any other attribute −\n", 723 | "\n", 724 | "* **`__dict__`** : Dictionary containing the class's namespace. \n", 725 | "* **`__doc__`** : Class documentation string or none, if undefined. \n", 726 | "* **`__name__`** : Class name. \n", 727 | "* **`__module__`** : Module name in which the class is defined. This attribute is **`__main__`** in interactive mode. \n", 728 | "* **`__bases__`** : A possibly empty tuple containing the base classes, in the order of their occurrence in the base class list.\n", 729 | "\n", 730 | "let us try to access all these attributes" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": 13, 736 | "metadata": { 737 | "ExecuteTime": { 738 | "end_time": "2021-06-20T19:10:05.676059Z", 739 | "start_time": "2021-06-20T19:10:05.651650Z" 740 | }, 741 | "scrolled": true 742 | }, 743 | "outputs": [ 744 | { 745 | "name": "stdout", 746 | "output_type": "stream", 747 | "text": [ 748 | "Total Student : 3\n", 749 | "Student.__doc__: Common base class for all students\n", 750 | "StudentStudent.__name__: Student\n", 751 | "Student.__module__: __main__\n", 752 | "Student.__bases__: (,)\n", 753 | "Student.__dict__: {'__module__': '__main__', '__doc__': 'Common base class for all students', 'student_count': 3, '__init__': , 'printStudentData': , '__dict__': , '__weakref__': }\n" 754 | ] 755 | } 756 | ], 757 | "source": [ 758 | "# Example 1:\n", 759 | "\n", 760 | "class Student:\n", 761 | " 'Common base class for all students'\n", 762 | " student_count=0\n", 763 | "\n", 764 | " def __init__(self, name, id):\n", 765 | " self.name = name\n", 766 | " self.id = id\n", 767 | " Student.student_count+=1\n", 768 | "\n", 769 | " def printStudentData(self):\n", 770 | " print (\"Name : \", self.name, \", Id : \", self.id)\n", 771 | "\n", 772 | "std1=Student(\"Milan\",101)\n", 773 | "std2=Student(\"Vijay\",102)\n", 774 | "std3=Student(\"Chirag\",103)\n", 775 | "\n", 776 | "print(\"Total Student : \",Student.student_count)\n", 777 | "print (\"Student.__doc__:\", Student.__doc__)\n", 778 | "print (\"StudentStudent.__name__:\", Student.__name__)\n", 779 | "print (\"Student.__module__:\", Student.__module__)\n", 780 | "print (\"Student.__bases__:\", Student.__bases__)\n", 781 | "print (\"Student.__dict__:\", Student.__dict__)" 782 | ] 783 | }, 784 | { 785 | "cell_type": "markdown", 786 | "metadata": {}, 787 | "source": [ 788 | "## Object Properties\n", 789 | "\n", 790 | "Every object has properties with it. In other words, we can say that object property is an association between **name** and **value**.\n", 791 | "\n", 792 | "For example, a car is an object, and its properties are car color, sunroof, price, manufacture, model, engine, and so on. Here, color is the name and red is the **value**." 793 | ] 794 | }, 795 | { 796 | "cell_type": "markdown", 797 | "metadata": {}, 798 | "source": [ 799 | "### Modify Object Properties\n", 800 | "\n", 801 | "Every object has properties associated with them. We can set or modify the object’s properties after object initialization by calling the property directly using the dot operator." 802 | ] 803 | }, 804 | { 805 | "cell_type": "code", 806 | "execution_count": 14, 807 | "metadata": { 808 | "ExecuteTime": { 809 | "end_time": "2021-06-20T19:10:06.812770Z", 810 | "start_time": "2021-06-20T19:10:06.803984Z" 811 | } 812 | }, 813 | "outputs": [ 814 | { 815 | "name": "stdout", 816 | "output_type": "stream", 817 | "text": [ 818 | "Fruit is strawberry and Color is red\n" 819 | ] 820 | } 821 | ], 822 | "source": [ 823 | "# Example 1:\n", 824 | "\n", 825 | "class Fruit:\n", 826 | " def __init__(self, name, color):\n", 827 | " self.name = name\n", 828 | " self.color = color\n", 829 | "\n", 830 | " def show(self):\n", 831 | " print(\"Fruit is\", self.name, \"and Color is\", self.color)\n", 832 | "\n", 833 | "# creating object of the class\n", 834 | "obj = Fruit(\"Apple\", \"red\")\n", 835 | "\n", 836 | "# Modifying Object Properties\n", 837 | "obj.name = \"strawberry\"\n", 838 | "\n", 839 | "# calling the instance method using the object obj\n", 840 | "obj.show() # Output Fruit is strawberry and Color is red" 841 | ] 842 | }, 843 | { 844 | "cell_type": "markdown", 845 | "metadata": {}, 846 | "source": [ 847 | "### Delete object properties\n", 848 | "\n", 849 | "We can delete the object property by using the **`del`** keyword. After deleting it, if we try to access it, we will get an error." 850 | ] 851 | }, 852 | { 853 | "cell_type": "code", 854 | "execution_count": 15, 855 | "metadata": { 856 | "ExecuteTime": { 857 | "end_time": "2021-06-20T19:10:07.719989Z", 858 | "start_time": "2021-06-20T19:10:07.698507Z" 859 | }, 860 | "scrolled": true 861 | }, 862 | "outputs": [ 863 | { 864 | "ename": "AttributeError", 865 | "evalue": "'Fruit' object has no attribute 'name'", 866 | "output_type": "error", 867 | "traceback": [ 868 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 869 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 870 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 16\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 17\u001b[0m \u001b[1;31m# Accessing object properties after deleting\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Output: AttributeError: 'Fruit' object has no attribute 'name'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 871 | "\u001b[1;31mAttributeError\u001b[0m: 'Fruit' object has no attribute 'name'" 872 | ] 873 | } 874 | ], 875 | "source": [ 876 | "# Example 1:\n", 877 | "\n", 878 | "class Fruit:\n", 879 | " def __init__(self, name, color):\n", 880 | " self.name = name\n", 881 | " self.color = color\n", 882 | "\n", 883 | " def show(self):\n", 884 | " print(\"Fruit is\", self.name, \"and Color is\", self.color)\n", 885 | "\n", 886 | "# creating object of the class\n", 887 | "obj = Fruit(\"Apple\", \"red\")\n", 888 | "\n", 889 | "# Deleting Object Properties\n", 890 | "del obj.name\n", 891 | "\n", 892 | "# Accessing object properties after deleting\n", 893 | "print(obj.name) # Output: AttributeError: 'Fruit' object has no attribute 'name'" 894 | ] 895 | }, 896 | { 897 | "cell_type": "markdown", 898 | "metadata": {}, 899 | "source": [ 900 | "## Delete Objects\n", 901 | "\n", 902 | "In Python, we can also delete the object by using a **`del`** keyword. An object can be anything like, class object, list, tuple, set, etc.\n", 903 | "\n", 904 | "**Syntax:**\n", 905 | "\n", 906 | "```python\n", 907 | "del object_name\n", 908 | "```" 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": 16, 914 | "metadata": { 915 | "ExecuteTime": { 916 | "end_time": "2021-06-20T19:10:09.470950Z", 917 | "start_time": "2021-06-20T19:10:09.444584Z" 918 | } 919 | }, 920 | "outputs": [ 921 | { 922 | "name": "stdout", 923 | "output_type": "stream", 924 | "text": [ 925 | "Department is IT\n" 926 | ] 927 | }, 928 | { 929 | "ename": "NameError", 930 | "evalue": "name 'emp' is not defined", 931 | "output_type": "error", 932 | "traceback": [ 933 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 934 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 935 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[1;31m# Accessing after delete object\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 16\u001b[1;33m \u001b[0memp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Output : NameError: name 'emp' is not defined\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 936 | "\u001b[1;31mNameError\u001b[0m: name 'emp' is not defined" 937 | ] 938 | } 939 | ], 940 | "source": [ 941 | "# Example 1:\n", 942 | "\n", 943 | "class Employee:\n", 944 | " depatment = \"IT\"\n", 945 | "\n", 946 | " def show(self):\n", 947 | " print(\"Department is \", self.depatment)\n", 948 | "\n", 949 | "emp = Employee()\n", 950 | "emp.show()\n", 951 | "\n", 952 | "# delete object\n", 953 | "del emp\n", 954 | "\n", 955 | "# Accessing after delete object\n", 956 | "emp.show() # Output : NameError: name 'emp' is not defined " 957 | ] 958 | }, 959 | { 960 | "cell_type": "markdown", 961 | "metadata": {}, 962 | "source": [ 963 | "**Explanation:**\n", 964 | "\n", 965 | "In the above example, we create the object **`emp`** of the class **`Employee`**. After that, using the **`del`** keyword, we deleted that object." 966 | ] 967 | }, 968 | { 969 | "cell_type": "code", 970 | "execution_count": 17, 971 | "metadata": { 972 | "ExecuteTime": { 973 | "end_time": "2021-06-20T19:10:10.872305Z", 974 | "start_time": "2021-06-20T19:10:10.843014Z" 975 | } 976 | }, 977 | "outputs": [ 978 | { 979 | "ename": "AttributeError", 980 | "evalue": "'ComplexNumber' object has no attribute 'imag'", 981 | "output_type": "error", 982 | "traceback": [ 983 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 984 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 985 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mnum1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mComplexNumber\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mdel\u001b[0m \u001b[0mnum1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mimag\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mnum1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_data\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[0m", 986 | "\u001b[1;32m\u001b[0m in \u001b[0;36mget_data\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mget_data\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\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----> 9\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf'{self.real}+{self.imag}j'\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 10\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;31m# Create a new ComplexNumber object\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 987 | "\u001b[1;31mAttributeError\u001b[0m: 'ComplexNumber' object has no attribute 'imag'" 988 | ] 989 | } 990 | ], 991 | "source": [ 992 | "# Example 2:\n", 993 | "\n", 994 | "num1 = ComplexNumber(2,3)\n", 995 | "del num1.imag\n", 996 | "num1.get_data()" 997 | ] 998 | }, 999 | { 1000 | "cell_type": "code", 1001 | "execution_count": 18, 1002 | "metadata": { 1003 | "ExecuteTime": { 1004 | "end_time": "2021-06-20T19:10:11.381090Z", 1005 | "start_time": "2021-06-20T19:10:11.357656Z" 1006 | } 1007 | }, 1008 | "outputs": [ 1009 | { 1010 | "ename": "AttributeError", 1011 | "evalue": "'ComplexNumber' object has no attribute 'get_data'", 1012 | "output_type": "error", 1013 | "traceback": [ 1014 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 1015 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 1016 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mdel\u001b[0m \u001b[0mComplexNumber\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_data\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mnum1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_data\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[0m", 1017 | "\u001b[1;31mAttributeError\u001b[0m: 'ComplexNumber' object has no attribute 'get_data'" 1018 | ] 1019 | } 1020 | ], 1021 | "source": [ 1022 | "# Example 3:\n", 1023 | "\n", 1024 | "del ComplexNumber.get_data\n", 1025 | "num1.get_data()" 1026 | ] 1027 | }, 1028 | { 1029 | "cell_type": "markdown", 1030 | "metadata": {}, 1031 | "source": [ 1032 | "We can even delete the object itself, using the del statement!" 1033 | ] 1034 | }, 1035 | { 1036 | "cell_type": "code", 1037 | "execution_count": 19, 1038 | "metadata": { 1039 | "ExecuteTime": { 1040 | "end_time": "2021-06-20T19:10:12.993385Z", 1041 | "start_time": "2021-06-20T19:10:12.978737Z" 1042 | } 1043 | }, 1044 | "outputs": [ 1045 | { 1046 | "ename": "NameError", 1047 | "evalue": "name 'c1' is not defined", 1048 | "output_type": "error", 1049 | "traceback": [ 1050 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 1051 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 1052 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mc1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mComplexNumber\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mdel\u001b[0m \u001b[0mc1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mc1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 1053 | "\u001b[1;31mNameError\u001b[0m: name 'c1' is not defined" 1054 | ] 1055 | } 1056 | ], 1057 | "source": [ 1058 | "# Example 4:\n", 1059 | "\n", 1060 | "c1 = ComplexNumber(1,3)\n", 1061 | "del c1\n", 1062 | "c1" 1063 | ] 1064 | }, 1065 | { 1066 | "cell_type": "markdown", 1067 | "metadata": {}, 1068 | "source": [ 1069 | "**Explanation:**\n", 1070 | "\n", 1071 | "Actually, it is more complicated than that. When we do **`c1 = ComplexNumber(1,3)`**, a new instance object is created in memory and the name **`c1`** binds with it.\n", 1072 | "\n", 1073 | "On the command **`del c1`**, this binding is removed and the name **`c1`** is deleted from the corresponding namespace. The object however continues to exist in memory and if no other name is bound to it, it is later automatically destroyed.\n", 1074 | "\n", 1075 | "This automatic destruction of unreferenced objects in Python is also called garbage collection.\n", 1076 | "\n", 1077 | "
\n", 1078 | "\n", 1079 | "
" 1080 | ] 1081 | }, 1082 | { 1083 | "cell_type": "markdown", 1084 | "metadata": {}, 1085 | "source": [ 1086 | "## Data Hiding\n", 1087 | "\n", 1088 | "An object's attributes may or may not be visible outside the class definition. You need to name attributes with a double underscore prefix, and those attributes then will not be directly **visible** to outsiders." 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "code", 1093 | "execution_count": 20, 1094 | "metadata": { 1095 | "ExecuteTime": { 1096 | "end_time": "2021-06-20T19:10:15.081745Z", 1097 | "start_time": "2021-06-20T19:10:15.060263Z" 1098 | }, 1099 | "scrolled": true 1100 | }, 1101 | "outputs": [ 1102 | { 1103 | "name": "stdout", 1104 | "output_type": "stream", 1105 | "text": [ 1106 | "1\n", 1107 | "2\n" 1108 | ] 1109 | }, 1110 | { 1111 | "ename": "AttributeError", 1112 | "evalue": "'JustCounter' object has no attribute '__secretCount'", 1113 | "output_type": "error", 1114 | "traceback": [ 1115 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 1116 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 1117 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcount\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 10\u001b[0m \u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcount\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---> 11\u001b[1;33m \u001b[0mprint\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__secretCount\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 1118 | "\u001b[1;31mAttributeError\u001b[0m: 'JustCounter' object has no attribute '__secretCount'" 1119 | ] 1120 | } 1121 | ], 1122 | "source": [ 1123 | "class JustCounter:\n", 1124 | " __secretCount = 0 # private attribute\n", 1125 | "\n", 1126 | " def count(self):\n", 1127 | " self.__secretCount += 1\n", 1128 | " print (self.__secretCount)\n", 1129 | "\n", 1130 | "counter = JustCounter()\n", 1131 | "counter.count()\n", 1132 | "counter.count()\n", 1133 | "print (counter.__secretCount)" 1134 | ] 1135 | }, 1136 | { 1137 | "cell_type": "markdown", 1138 | "metadata": {}, 1139 | "source": [ 1140 | "Python protects those members by internally changing the name to include the class name. You can access such attributes as **`object._className__attrName`**. If you would replace your last line as following, then it works for you −" 1141 | ] 1142 | }, 1143 | { 1144 | "cell_type": "code", 1145 | "execution_count": 21, 1146 | "metadata": { 1147 | "ExecuteTime": { 1148 | "end_time": "2021-06-20T19:10:16.999698Z", 1149 | "start_time": "2021-06-20T19:10:16.979193Z" 1150 | } 1151 | }, 1152 | "outputs": [ 1153 | { 1154 | "name": "stdout", 1155 | "output_type": "stream", 1156 | "text": [ 1157 | "1\n", 1158 | "2\n", 1159 | "2\n" 1160 | ] 1161 | } 1162 | ], 1163 | "source": [ 1164 | "class JustCounter:\n", 1165 | " __secretCount = 0\n", 1166 | "\n", 1167 | " def count(self):\n", 1168 | " self.__secretCount += 1\n", 1169 | " print (self.__secretCount)\n", 1170 | "\n", 1171 | "counter = JustCounter()\n", 1172 | "counter.count()\n", 1173 | "counter.count()\n", 1174 | "# print (counter.__secretCount) # This wont work\n", 1175 | "print (counter._JustCounter__secretCount)" 1176 | ] 1177 | }, 1178 | { 1179 | "cell_type": "code", 1180 | "execution_count": 22, 1181 | "metadata": {}, 1182 | "outputs": [ 1183 | { 1184 | "name": "stdout", 1185 | "output_type": "stream", 1186 | "text": [ 1187 | "{'a': 5, 'b': 7, 'c': {'d': 8}}\n" 1188 | ] 1189 | } 1190 | ], 1191 | "source": [ 1192 | "# convert_dictionary_to_python_object\n", 1193 | "\n", 1194 | "class obj(object):\n", 1195 | " def __init__(self, d):\n", 1196 | " for x, y in d.items():\n", 1197 | " setattr(self, x, obj(y) if isinstance(y, dict) else y)\n", 1198 | "data = {'a':5,'b':7,'c':{'d':8}}\n", 1199 | "ob = obj(data)\n", 1200 | "print(data)" 1201 | ] 1202 | }, 1203 | { 1204 | "cell_type": "code", 1205 | "execution_count": null, 1206 | "metadata": {}, 1207 | "outputs": [], 1208 | "source": [] 1209 | } 1210 | ], 1211 | "metadata": { 1212 | "hide_input": false, 1213 | "kernelspec": { 1214 | "display_name": "Python 3", 1215 | "language": "python", 1216 | "name": "python3" 1217 | }, 1218 | "language_info": { 1219 | "codemirror_mode": { 1220 | "name": "ipython", 1221 | "version": 3 1222 | }, 1223 | "file_extension": ".py", 1224 | "mimetype": "text/x-python", 1225 | "name": "python", 1226 | "nbconvert_exporter": "python", 1227 | "pygments_lexer": "ipython3", 1228 | "version": "3.8.8" 1229 | }, 1230 | "toc": { 1231 | "base_numbering": 1, 1232 | "nav_menu": {}, 1233 | "number_sections": true, 1234 | "sideBar": true, 1235 | "skip_h1_title": false, 1236 | "title_cell": "Table of Contents", 1237 | "title_sidebar": "Contents", 1238 | "toc_cell": false, 1239 | "toc_position": {}, 1240 | "toc_section_display": true, 1241 | "toc_window_display": false 1242 | }, 1243 | "varInspector": { 1244 | "cols": { 1245 | "lenName": 16, 1246 | "lenType": 16, 1247 | "lenVar": 40 1248 | }, 1249 | "kernels_config": { 1250 | "python": { 1251 | "delete_cmd_postfix": "", 1252 | "delete_cmd_prefix": "del ", 1253 | "library": "var_list.py", 1254 | "varRefreshCmd": "print(var_dic_list())" 1255 | }, 1256 | "r": { 1257 | "delete_cmd_postfix": ") ", 1258 | "delete_cmd_prefix": "rm(", 1259 | "library": "var_list.r", 1260 | "varRefreshCmd": "cat(var_dic_list()) " 1261 | } 1262 | }, 1263 | "types_to_exclude": [ 1264 | "module", 1265 | "function", 1266 | "builtin_function_or_method", 1267 | "instance", 1268 | "_Feature" 1269 | ], 1270 | "window_display": false 1271 | } 1272 | }, 1273 | "nbformat": 4, 1274 | "nbformat_minor": 2 1275 | } 1276 | -------------------------------------------------------------------------------- /003_Python_Inheritance.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 | "# Python Inheritance\n", 17 | "\n", 18 | "The **process of inheriting the properties of the base (or parent) class into a derived (or child) class is called inheritance**. Inheritance enables us to define a class that takes all the functionality from a base class and allows us to add more. In this tutorial, you will learn to use inheritance in Python." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Inheritance in Python\n", 26 | "\n", 27 | "Inheritance is a powerful feature in object oriented programming. The main purpose of inheritance is the reusability of code because we can use the existing class to create a new class instead of creating it from scratch.\n", 28 | "\n", 29 | "It refers to defining a new [**class**](https://github.com/milaan9/06_Python_Object_Class/blob/main/002_Python_Classes_and_Objects.ipynb) with little or no modification to an existing class. The new class is called **derived (or child)** class and the one from which it inherits is called the **base (or parent)** class.\n", 30 | "\n", 31 | "In inheritance, the derived class acquires and access all the data members, properties, and functions from the base class. Also, a derived class can also provide its specific implementation to the methods of the base class.\n", 32 | "\n", 33 | "
\n", 34 | "\n", 35 | "
" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "**Syntax:**\n", 43 | "\n", 44 | "```python\n", 45 | "class BaseClass:\n", 46 | " Body of base class\n", 47 | "class DerivedClass(BaseClass):\n", 48 | " Body of derived class\n", 49 | "```" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Types Of Inheritance\n", 57 | "\n", 58 | "In Python, based upon the number of child and parent classes involved, there are five types of inheritance. The type of inheritance are listed below:\n", 59 | "\n", 60 | "1. Single Inheritance\n", 61 | "2. Multiple Inheritance\n", 62 | "3. Multilevel inheritance\n", 63 | "4. Hierarchical Inheritance\n", 64 | "5. Hybrid Inheritance\n", 65 | "\n", 66 | "Now let’s see each in detail with example." 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "## Python Single Inheritance\n", 74 | "\n", 75 | "In single inheritance, a derived class inherits from a single-base class. Here in one derived class and one base class.\n", 76 | "\n", 77 | "
\n", 78 | "\n", 79 | "
\n", 80 | "\n", 81 | "Here, the **`Derived`** class is derived from **`Base`** class." 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 1, 87 | "metadata": { 88 | "ExecuteTime": { 89 | "end_time": "2021-06-20T19:10:28.178811Z", 90 | "start_time": "2021-06-20T19:10:28.165139Z" 91 | } 92 | }, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "Inside Vehicle class\n", 99 | "Inside Car class\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "# Example 1: Single Inheritance\n", 105 | "\n", 106 | "# Base class\n", 107 | "class Vehicle:\n", 108 | " def Vehicle_info(self):\n", 109 | " print('Inside Vehicle class')\n", 110 | "\n", 111 | "# Derived class\n", 112 | "class Car(Vehicle):\n", 113 | " def car_info(self):\n", 114 | " print('Inside Car class')\n", 115 | "\n", 116 | "# Create object of Car\n", 117 | "car = Car()\n", 118 | "\n", 119 | "# access Vehicle's info using car object\n", 120 | "car.Vehicle_info()\n", 121 | "car.car_info()" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "## Python Multiple Inheritance\n", 129 | "\n", 130 | "In multiple inheritance, one derived class can inherit from multiple base classes. \n", 131 | "\n", 132 | "
\n", 133 | "\n", 134 | "
\n", 135 | "\n", 136 | "Here, the **`MultiDerived`** class is derived from **`Base1`** and **`Base2`** classes." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 2, 142 | "metadata": { 143 | "ExecuteTime": { 144 | "end_time": "2021-06-20T19:10:31.094801Z", 145 | "start_time": "2021-06-20T19:10:31.069415Z" 146 | } 147 | }, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "Inside Person class\n", 154 | "Name: Milaan Age: 33\n", 155 | "Inside Company class\n", 156 | "Name: Google location: Atlanta\n", 157 | "Inside Employee class\n", 158 | "Salary: 19000 Skill: Machine Learning\n" 159 | ] 160 | } 161 | ], 162 | "source": [ 163 | "# Example 1: Multiple Inheritance\n", 164 | "\n", 165 | "# Base class 1\n", 166 | "class Person:\n", 167 | " def person_info(self, name, age):\n", 168 | " print('Inside Person class')\n", 169 | " print('Name:', name, 'Age:', age)\n", 170 | "\n", 171 | "# Base class 2\n", 172 | "class Company:\n", 173 | " def company_info(self, company_name, location):\n", 174 | " print('Inside Company class')\n", 175 | " print('Name:', company_name, 'location:', location)\n", 176 | "\n", 177 | "# Derived class\n", 178 | "class Employee(Person, Company):\n", 179 | " def Employee_info(self, salary, skill):\n", 180 | " print('Inside Employee class')\n", 181 | " print('Salary:', salary, 'Skill:', skill)\n", 182 | "\n", 183 | "# Create object of Employee\n", 184 | "emp = Employee()\n", 185 | "\n", 186 | "# access data\n", 187 | "emp.person_info('Milaan', 33)\n", 188 | "emp.company_info('Google', 'Atlanta')\n", 189 | "emp.Employee_info(19000, 'Machine Learning')" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 3, 195 | "metadata": { 196 | "ExecuteTime": { 197 | "end_time": "2021-06-20T19:10:31.849192Z", 198 | "start_time": "2021-06-20T19:10:31.830639Z" 199 | }, 200 | "scrolled": false 201 | }, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "first\n", 208 | "second\n", 209 | "third\n" 210 | ] 211 | } 212 | ], 213 | "source": [ 214 | "# Example 2:\n", 215 | "\n", 216 | "class First(object): # Base class1\n", 217 | " def __init__(self):\n", 218 | " super(First, self).__init__()\n", 219 | " print(\"first\")\n", 220 | "\n", 221 | "class Second(object): # Base class2\n", 222 | " def __init__(self):\n", 223 | " super(Second, self).__init__()\n", 224 | " print(\"second\")\n", 225 | "\n", 226 | "class Third(Second, First): # Derived class derived from Base class 1 and Base class 2\n", 227 | " def __init__(self):\n", 228 | " super(Third, self).__init__()\n", 229 | " print(\"third\")\n", 230 | "\n", 231 | "Third(); #call Third class constructor" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 4, 237 | "metadata": { 238 | "ExecuteTime": { 239 | "end_time": "2021-06-20T19:10:32.497625Z", 240 | "start_time": "2021-06-20T19:10:32.481024Z" 241 | } 242 | }, 243 | "outputs": [ 244 | { 245 | "name": "stdout", 246 | "output_type": "stream", 247 | "text": [ 248 | "Dog has four legs.\n", 249 | "Dog is a warm-blooded animal.\n" 250 | ] 251 | } 252 | ], 253 | "source": [ 254 | "# Example 3:\n", 255 | "\n", 256 | "class Mammal(object):\n", 257 | " def __init__(self, mammalName):\n", 258 | " print(mammalName, 'is a warm-blooded animal.')\n", 259 | " \n", 260 | "class Dog(Mammal):\n", 261 | " def __init__(self):\n", 262 | " print('Dog has four legs.')\n", 263 | " super().__init__('Dog')\n", 264 | " \n", 265 | "d1 = Dog()" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "**Explanation**:\n", 273 | "\n", 274 | "Here, we called the **`__init__()`** method of the **`Mammal`** class (from the **`Dog`** class) using code\n", 275 | "\n", 276 | "**`super().__init__('Dog')`**\n", 277 | "\n", 278 | "instead of\n", 279 | "\n", 280 | "**`Mammal.__init__(self, 'Dog')`**\n", 281 | "\n", 282 | "Since we do not need to specify the name of the base class when we call its members, we can easily change the base class name (if we need to).\n", 283 | "\n", 284 | "```python\n", 285 | "# changing base class to CanidaeFamily\n", 286 | "class Dog(CanidaeFamily):\n", 287 | " def __init__(self):\n", 288 | " print('Dog has four legs.')\n", 289 | "\n", 290 | " # no need to change this\n", 291 | " super().__init__('Dog')\n", 292 | "```" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": {}, 298 | "source": [ 299 | "The **[super()](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/068_Python_super%28%29.ipynb)** built-in function returns a proxy object, a substitute object that can call methods of the base class via delegation. This is called indirection (ability to reference base object with **`super()`** built-in function).\n", 300 | "\n", 301 | "Since the indirection is computed at the runtime, we can use different base classes at different times (if we need to)." 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 5, 307 | "metadata": { 308 | "ExecuteTime": { 309 | "end_time": "2021-06-20T19:10:34.033744Z", 310 | "start_time": "2021-06-20T19:10:34.014214Z" 311 | } 312 | }, 313 | "outputs": [ 314 | { 315 | "name": "stdout", 316 | "output_type": "stream", 317 | "text": [ 318 | "Dog has 4 legs.\n", 319 | "Dog can't swim.\n", 320 | "Dog can't fly.\n", 321 | "Dog is a warm-blooded animal.\n", 322 | "Dog is an animal.\n", 323 | "\n", 324 | "Bat can't swim.\n", 325 | "Bat is a warm-blooded animal.\n", 326 | "Bat is an animal.\n" 327 | ] 328 | } 329 | ], 330 | "source": [ 331 | "# Example 4:\n", 332 | "\n", 333 | "class Animal:\n", 334 | " def __init__(self, Animal):\n", 335 | " print(Animal, 'is an animal.');\n", 336 | "\n", 337 | "class Mammal(Animal): # Mammal derived to Animal\n", 338 | " def __init__(self, mammalName):\n", 339 | " print(mammalName, 'is a warm-blooded animal.')\n", 340 | " super().__init__(mammalName)\n", 341 | " \n", 342 | "class NonWingedMammal(Mammal): # NonWingedMammal derived from Mammal (derived from Animal)\n", 343 | " def __init__(self, NonWingedMammal):\n", 344 | " print(NonWingedMammal, \"can't fly.\")\n", 345 | " super().__init__(NonWingedMammal)\n", 346 | "\n", 347 | "class NonMarineMammal(Mammal): # NonMarineMammal derived from Mammal (derived from Animal)\n", 348 | " def __init__(self, NonMarineMammal):\n", 349 | " print(NonMarineMammal, \"can't swim.\")\n", 350 | " super().__init__(NonMarineMammal)\n", 351 | "\n", 352 | "class Dog(NonMarineMammal, NonWingedMammal): # Dog derived from NonMarineMammal and NonWingedMammal\n", 353 | " def __init__(self):\n", 354 | " print('Dog has 4 legs.');\n", 355 | " super().__init__('Dog')\n", 356 | " \n", 357 | "d = Dog()\n", 358 | "print('')\n", 359 | "bat = NonMarineMammal('Bat')" 360 | ] 361 | }, 362 | { 363 | "cell_type": "markdown", 364 | "metadata": {}, 365 | "source": [ 366 | "### Why `super()` keyword\n", 367 | "\n", 368 | "The **`super()`** function is most commonly used with **`__init__`** function in base class. This is usually the only place where we need to do some things in a child then complete the **initialization** in the **parent**.\n", 369 | "\n", 370 | "```python\n", 371 | "class Child(Parent):\n", 372 | " def __init__(self, stuff)\n", 373 | " self.stuff = stuff\n", 374 | " super(Child, self).__init__()\n", 375 | "```" 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "### Private members of parent class\n", 383 | "\n", 384 | "We don’t always want the instance variables of the parent class to be inherited by the child class i.e. we can make some of the instance variables of the parent class private, which won’t be available to the child class. We can make an instance variable by adding double underscores before its name. " 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": 6, 390 | "metadata": { 391 | "ExecuteTime": { 392 | "end_time": "2021-06-20T19:10:36.334505Z", 393 | "start_time": "2021-06-20T19:10:36.021035Z" 394 | }, 395 | "scrolled": false 396 | }, 397 | "outputs": [ 398 | { 399 | "ename": "AttributeError", 400 | "evalue": "type object 'D' has no attribute 'd'", 401 | "output_type": "error", 402 | "traceback": [ 403 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 404 | "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", 405 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 16\u001b[0m \u001b[0mobject1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mD\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 17\u001b[0m \u001b[1;31m# produces an error as d is private instance variable\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mD\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 406 | "\u001b[1;31mAttributeError\u001b[0m: type object 'D' has no attribute 'd'" 407 | ] 408 | } 409 | ], 410 | "source": [ 411 | "## Example:\n", 412 | "\n", 413 | "# Python program to demonstrate private members\n", 414 | "# of the parent class\n", 415 | "class C(object):\n", 416 | " def __init__(self):\n", 417 | " self.c = 21\n", 418 | " # d is private instance variable\n", 419 | " self.__d = 42 # Note: before 'd' there are two '_'\n", 420 | "\n", 421 | "class D(C):\n", 422 | " def __init__(self):\n", 423 | " self.e = 84\n", 424 | " C.__init__(self)\n", 425 | "\n", 426 | "object1 = D()\n", 427 | "# produces an error as d is private instance variable\n", 428 | "print(D.d)" 429 | ] 430 | }, 431 | { 432 | "cell_type": "markdown", 433 | "metadata": {}, 434 | "source": [ 435 | "## Python Multilevel Inheritance\n", 436 | "\n", 437 | "In multilevel inheritance, we can also inherit from a derived class. It can be of any depth in Python.\n", 438 | "\n", 439 | "All the features of the base class and the derived class are inherited into the new derived class.\n", 440 | "\n", 441 | "For example, there are three classes A, B, C. A is the superclass, B is the child class of A, C is the child class of B. In other words, we can say a **chain of classes** is called multilevel inheritance.\n", 442 | "\n", 443 | "
\n", 444 | "\n", 445 | "
\n", 446 | "\n", 447 | "\n", 448 | "Here, the **`Derived1`** class is derived from the **`Base`** class, and the **`Derived2`** class is derived from the **`Derived1`** class." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 7, 454 | "metadata": { 455 | "ExecuteTime": { 456 | "end_time": "2021-06-20T19:10:37.424343Z", 457 | "start_time": "2021-06-20T19:10:37.413604Z" 458 | } 459 | }, 460 | "outputs": [ 461 | { 462 | "name": "stdout", 463 | "output_type": "stream", 464 | "text": [ 465 | "Inside Vehicle class\n", 466 | "Inside Car class\n", 467 | "Inside SportsCar class\n" 468 | ] 469 | } 470 | ], 471 | "source": [ 472 | "# Example 1:\n", 473 | "\n", 474 | "# Base class\n", 475 | "class Vehicle:\n", 476 | " def Vehicle_info(self):\n", 477 | " print('Inside Vehicle class')\n", 478 | "\n", 479 | "# Child class\n", 480 | "class Car(Vehicle):\n", 481 | " def car_info(self):\n", 482 | " print('Inside Car class')\n", 483 | "\n", 484 | "# Child class\n", 485 | "class SportsCar(Car):\n", 486 | " def sports_car_info(self):\n", 487 | " print('Inside SportsCar class')\n", 488 | "\n", 489 | "# Create object of SportsCar\n", 490 | "s_car = SportsCar()\n", 491 | "\n", 492 | "# access Vehicle's and Car info using SportsCar object\n", 493 | "s_car.Vehicle_info()\n", 494 | "s_car.car_info()\n", 495 | "s_car.sports_car_info()\n" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": 8, 501 | "metadata": { 502 | "ExecuteTime": { 503 | "end_time": "2021-06-20T19:10:38.178241Z", 504 | "start_time": "2021-06-20T19:10:38.156761Z" 505 | } 506 | }, 507 | "outputs": [ 508 | { 509 | "name": "stdout", 510 | "output_type": "stream", 511 | "text": [ 512 | "Eating...\n", 513 | "Barking...\n", 514 | "Weeping...\n" 515 | ] 516 | } 517 | ], 518 | "source": [ 519 | "# Example 2:\n", 520 | "\n", 521 | "class Animal: # grandparent class\n", 522 | " def eat(self):\n", 523 | " print('Eating...')\n", 524 | "\n", 525 | "class Dog(Animal): # parent class\n", 526 | " def bark(self):\n", 527 | " print('Barking...')\n", 528 | "\n", 529 | "class BabyDog(Dog): # child class\n", 530 | " def weep(self):\n", 531 | " print('Weeping...')\n", 532 | "\n", 533 | "d=BabyDog()\n", 534 | "d.eat()\n", 535 | "d.bark()\n", 536 | "d.weep()" 537 | ] 538 | }, 539 | { 540 | "cell_type": "markdown", 541 | "metadata": {}, 542 | "source": [ 543 | "## Hierarchical Inheritance\n", 544 | "\n", 545 | "In Hierarchical inheritance, more than one child class is derived from a single parent class. In other words, we can say one base class and multiple derived classes.\n", 546 | "\n", 547 | "
\n", 548 | "\n", 549 | "
" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": 9, 555 | "metadata": { 556 | "ExecuteTime": { 557 | "end_time": "2021-06-20T19:10:39.548350Z", 558 | "start_time": "2021-06-20T19:10:39.526867Z" 559 | } 560 | }, 561 | "outputs": [ 562 | { 563 | "name": "stdout", 564 | "output_type": "stream", 565 | "text": [ 566 | "This is Vehicle\n", 567 | "Car name is: BMW\n", 568 | "This is Vehicle\n", 569 | "Truck name is: Ford\n" 570 | ] 571 | } 572 | ], 573 | "source": [ 574 | "# Example 1:\n", 575 | "\n", 576 | "class Vehicle:\n", 577 | " def info(self):\n", 578 | " print(\"This is Vehicle\")\n", 579 | "\n", 580 | "class Car(Vehicle):\n", 581 | " def car_info(self, name):\n", 582 | " print(\"Car name is:\", name)\n", 583 | "\n", 584 | "class Truck(Vehicle):\n", 585 | " def truck_info(self, name):\n", 586 | " print(\"Truck name is:\", name)\n", 587 | "\n", 588 | "obj1 = Car()\n", 589 | "obj1.info()\n", 590 | "obj1.car_info('BMW')\n", 591 | "\n", 592 | "obj2 = Truck()\n", 593 | "obj2.info()\n", 594 | "obj2.truck_info('Ford')" 595 | ] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "metadata": {}, 600 | "source": [ 601 | "## Hybrid Inheritance\n", 602 | "\n", 603 | "When inheritance is consists of multiple types or a combination of different inheritance is called hybrid inheritance.\n", 604 | "\n", 605 | "
\n", 606 | "\n", 607 | "
" 608 | ] 609 | }, 610 | { 611 | "cell_type": "code", 612 | "execution_count": 10, 613 | "metadata": { 614 | "ExecuteTime": { 615 | "end_time": "2021-06-20T19:10:40.928707Z", 616 | "start_time": "2021-06-20T19:10:40.909180Z" 617 | }, 618 | "scrolled": true 619 | }, 620 | "outputs": [ 621 | { 622 | "name": "stdout", 623 | "output_type": "stream", 624 | "text": [ 625 | "Inside Vehicle class\n", 626 | "Inside Car class\n", 627 | "Inside SportsCar class\n" 628 | ] 629 | } 630 | ], 631 | "source": [ 632 | "# Example 1:\n", 633 | "\n", 634 | "class Vehicle:\n", 635 | " def vehicle_info(self):\n", 636 | " print(\"Inside Vehicle class\")\n", 637 | "\n", 638 | "class Car(Vehicle):\n", 639 | " def car_info(self):\n", 640 | " print(\"Inside Car class\")\n", 641 | "\n", 642 | "class Truck(Vehicle):\n", 643 | " def truck_info(self):\n", 644 | " print(\"Inside Truck class\")\n", 645 | "\n", 646 | "# Sports Car can inherits properties of Vehicle and Car\n", 647 | "class SportsCar(Car, Vehicle):\n", 648 | " def sports_car_info(self):\n", 649 | " print(\"Inside SportsCar class\")\n", 650 | "\n", 651 | "# create object\n", 652 | "s_car = SportsCar()\n", 653 | "\n", 654 | "s_car.vehicle_info()\n", 655 | "s_car.car_info()\n", 656 | "s_car.sports_car_info()" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": {}, 662 | "source": [ 663 | "## Example of Inheritance in Python\n", 664 | "\n", 665 | "To demonstrate the use of inheritance, let us take an example.\n", 666 | "\n", 667 | "A polygon is a closed figure with 3 or more sides. Say, we have a class called **`Polygon`** defined as follows." 668 | ] 669 | }, 670 | { 671 | "cell_type": "code", 672 | "execution_count": 11, 673 | "metadata": { 674 | "ExecuteTime": { 675 | "end_time": "2021-06-20T19:10:42.466782Z", 676 | "start_time": "2021-06-20T19:10:42.446277Z" 677 | } 678 | }, 679 | "outputs": [], 680 | "source": [ 681 | "class Polygon:\n", 682 | " def __init__(self, no_of_sides):\n", 683 | " self.n = no_of_sides\n", 684 | " self.sides = [0 for i in range(no_of_sides)]\n", 685 | "\n", 686 | " def inputSides(self):\n", 687 | " self.sides = [float(input(\"Enter side \"+str(i+1)+\" : \")) for i in range(self.n)]\n", 688 | "\n", 689 | " def dispSides(self):\n", 690 | " for i in range(self.n):\n", 691 | " print(\"Side\",i+1,\"is\",self.sides[i])" 692 | ] 693 | }, 694 | { 695 | "cell_type": "markdown", 696 | "metadata": {}, 697 | "source": [ 698 | "\n", 699 | "\n", 700 | "This class has data attributes to store the number of sides **`n`** and magnitude of each side as a list called **`sides`**.\n", 701 | "\n", 702 | "The **`inputSides()`** method takes in the magnitude of each side and **`dispSides()`** displays these side lengths.\n", 703 | "\n", 704 | "A triangle is a polygon with 3 sides. So, we can create a class called **`Triangle`** which inherits from **`Polygon`**. This makes all the attributes of **`Polygon`** class available to the **`Triangle`** class.\n", 705 | "\n", 706 | "We don't need to define them again (code reusability). **`Triangle`** can be defined as follows." 707 | ] 708 | }, 709 | { 710 | "cell_type": "code", 711 | "execution_count": 12, 712 | "metadata": { 713 | "ExecuteTime": { 714 | "end_time": "2021-06-20T19:10:44.046846Z", 715 | "start_time": "2021-06-20T19:10:44.028297Z" 716 | } 717 | }, 718 | "outputs": [], 719 | "source": [ 720 | "class Triangle(Polygon):\n", 721 | " def __init__(self):\n", 722 | " Polygon.__init__(self,3)\n", 723 | "\n", 724 | " def findArea(self):\n", 725 | " a, b, c = self.sides\n", 726 | " # calculate the semi-perimeter\n", 727 | " s = (a + b + c) / 2\n", 728 | " area = (s*(s-a)*(s-b)*(s-c)) ** 0.5\n", 729 | " print('The area of the triangle is %0.2f' %area)" 730 | ] 731 | }, 732 | { 733 | "cell_type": "markdown", 734 | "metadata": {}, 735 | "source": [ 736 | "However, class **`Triangle`** has a new method **`findArea()`** to find and print the area of the triangle. Here is a sample run." 737 | ] 738 | }, 739 | { 740 | "cell_type": "code", 741 | "execution_count": 13, 742 | "metadata": { 743 | "ExecuteTime": { 744 | "end_time": "2021-06-20T19:11:05.921680Z", 745 | "start_time": "2021-06-20T19:10:52.909576Z" 746 | } 747 | }, 748 | "outputs": [ 749 | { 750 | "name": "stdout", 751 | "output_type": "stream", 752 | "text": [ 753 | "Enter side 1 : 5\n", 754 | "Enter side 2 : 3\n", 755 | "Enter side 3 : 6\n" 756 | ] 757 | } 758 | ], 759 | "source": [ 760 | "t = Triangle()\n", 761 | "t.inputSides()" 762 | ] 763 | }, 764 | { 765 | "cell_type": "code", 766 | "execution_count": 14, 767 | "metadata": { 768 | "ExecuteTime": { 769 | "end_time": "2021-06-20T19:11:09.085226Z", 770 | "start_time": "2021-06-20T19:11:09.064722Z" 771 | } 772 | }, 773 | "outputs": [ 774 | { 775 | "name": "stdout", 776 | "output_type": "stream", 777 | "text": [ 778 | "Side 1 is 5.0\n", 779 | "Side 2 is 3.0\n", 780 | "Side 3 is 6.0\n" 781 | ] 782 | } 783 | ], 784 | "source": [ 785 | "t.dispSides()" 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": 15, 791 | "metadata": { 792 | "ExecuteTime": { 793 | "end_time": "2021-06-20T19:11:10.205335Z", 794 | "start_time": "2021-06-20T19:11:10.189714Z" 795 | } 796 | }, 797 | "outputs": [ 798 | { 799 | "name": "stdout", 800 | "output_type": "stream", 801 | "text": [ 802 | "The area of the triangle is 7.48\n" 803 | ] 804 | } 805 | ], 806 | "source": [ 807 | "t.findArea()" 808 | ] 809 | }, 810 | { 811 | "cell_type": "markdown", 812 | "metadata": {}, 813 | "source": [ 814 | "We can see that even though we did not define methods like **`inputSides()`** or **`dispSides()`** for class **`Triangle`** separately, we were able to use them.\n", 815 | "\n", 816 | "If an attribute is not found in the class itself, the search continues to the base class. This repeats recursively, if the base class is itself derived from other classes." 817 | ] 818 | }, 819 | { 820 | "cell_type": "code", 821 | "execution_count": 16, 822 | "metadata": { 823 | "ExecuteTime": { 824 | "end_time": "2021-06-20T19:11:13.287342Z", 825 | "start_time": "2021-06-20T19:11:13.262931Z" 826 | }, 827 | "scrolled": false 828 | }, 829 | "outputs": [ 830 | { 831 | "name": "stdout", 832 | "output_type": "stream", 833 | "text": [ 834 | "Calling child constructor\n", 835 | "Calling child method\n", 836 | "Calling parent method\n", 837 | "Parent attribute : 200\n" 838 | ] 839 | } 840 | ], 841 | "source": [ 842 | "# Example 1:\n", 843 | "\n", 844 | "class Parent: # define parent class\n", 845 | " parentAttr = 100\n", 846 | "\n", 847 | " def __init__(self):\n", 848 | " print (\"Calling parent constructor\")\n", 849 | "\n", 850 | " def parentMethod(self):\n", 851 | " print ('Calling parent method')\n", 852 | "\n", 853 | " def setAttr(self, attr):\n", 854 | " Parent.parentAttr = attr\n", 855 | "\n", 856 | " def getAttr(self):\n", 857 | " print (\"Parent attribute :\", Parent.parentAttr)\n", 858 | "\n", 859 | "class Child(Parent): # define child class\n", 860 | " def __init__(self):\n", 861 | " print (\"Calling child constructor\")\n", 862 | "\n", 863 | " def childMethod(self):\n", 864 | " print ('Calling child method')\n", 865 | "\n", 866 | "c = Child() # instance of child\n", 867 | "c.childMethod() # child calls its method\n", 868 | "c.parentMethod() # calls parent's method\n", 869 | "c.setAttr(200) # again call parent's method\n", 870 | "c.getAttr() # again call parent's method" 871 | ] 872 | }, 873 | { 874 | "cell_type": "markdown", 875 | "metadata": {}, 876 | "source": [ 877 | "**Explanation:**\n", 878 | "\n", 879 | "In the above example, **hierarchical** and **multiple** inheritance exists. Here we created, parent class **`Vehicle`** and two child classes named **`Car`** and **`Truck`** this is hierarchical inheritance.\n", 880 | "\n", 881 | "Another is **`SportsCar`** inherit from two parent classes named **`Car`** and **`Vehicle`**. This is multiple inheritance." 882 | ] 883 | }, 884 | { 885 | "cell_type": "markdown", 886 | "metadata": {}, 887 | "source": [ 888 | "## Python `super()` function\n", 889 | "\n", 890 | "When a class inherits all properties and behavior from the parent class is called inheritance. In such a case, the inherited class is a subclass and the latter class is the parent class.\n", 891 | "\n", 892 | "In child class, we can refer to parent class by using the **`super()`** function. **[Python super() function](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)** returns a temporary object of the parent class that allows us to call a parent class method inside a child class method.\n", 893 | "\n", 894 | "Benefits of using the **`super()`** function are:\n", 895 | "\n", 896 | "* We are not required to remember or specify the parent class name to access its methods.\n", 897 | "* We can use the **`super()`** function in both **single** and **multiple** inheritances.\n", 898 | "* The **`super()`** function support code **reusability** as there is no need to write the entire function" 899 | ] 900 | }, 901 | { 902 | "cell_type": "code", 903 | "execution_count": 17, 904 | "metadata": { 905 | "ExecuteTime": { 906 | "end_time": "2021-06-20T19:11:16.481163Z", 907 | "start_time": "2021-06-20T19:11:16.462613Z" 908 | } 909 | }, 910 | "outputs": [ 911 | { 912 | "name": "stdout", 913 | "output_type": "stream", 914 | "text": [ 915 | "Arthur works at Google\n" 916 | ] 917 | } 918 | ], 919 | "source": [ 920 | "# Example 1:\n", 921 | "\n", 922 | "class Company:\n", 923 | " def company_name(self):\n", 924 | " return 'Google'\n", 925 | "\n", 926 | "class Employee(Company):\n", 927 | " def info(self):\n", 928 | " # Calling the superclass method using super()function\n", 929 | " c_name = super().company_name()\n", 930 | " print(\"Arthur works at\", c_name)\n", 931 | "\n", 932 | "# Creating object of child class\n", 933 | "emp = Employee()\n", 934 | "emp.info()" 935 | ] 936 | }, 937 | { 938 | "cell_type": "markdown", 939 | "metadata": {}, 940 | "source": [ 941 | "**Explanation:**\n", 942 | "\n", 943 | "In the above example, we create a parent class **`Company`** and child class **`Employee`**. In Employee class, we call the parent class method by using a **`super()`** function." 944 | ] 945 | }, 946 | { 947 | "cell_type": "markdown", 948 | "metadata": {}, 949 | "source": [ 950 | "## `issubclass()`\n", 951 | "\n", 952 | "In Python, we can verify whether a particular class is a subclass of another class. For this purpose, we can use **[Python issubclass() function](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/036_Python_issubclass().ipynb)**. This function returns **`True`** if the given class is the subclass of the specified class. Otherwise, it returns **`False`**.\n", 953 | "\n", 954 | "**Syntax:**\n", 955 | "\n", 956 | "```python\n", 957 | "issubclass(class, classinfo)\n", 958 | "```\n", 959 | "Where,\n", 960 | "\n", 961 | "* **`class`**: class to be checked.\n", 962 | "* **`classinfo`**: a class, type, or a tuple of classes or data types." 963 | ] 964 | }, 965 | { 966 | "cell_type": "code", 967 | "execution_count": 18, 968 | "metadata": { 969 | "ExecuteTime": { 970 | "end_time": "2021-06-20T19:11:19.120794Z", 971 | "start_time": "2021-06-20T19:11:19.103217Z" 972 | } 973 | }, 974 | "outputs": [ 975 | { 976 | "name": "stdout", 977 | "output_type": "stream", 978 | "text": [ 979 | "True\n", 980 | "False\n", 981 | "False\n", 982 | "True\n", 983 | "True\n" 984 | ] 985 | } 986 | ], 987 | "source": [ 988 | "# Example 1:\n", 989 | "\n", 990 | "class Company:\n", 991 | " def fun1(self):\n", 992 | " print(\"Inside parent class\")\n", 993 | "\n", 994 | "class Employee(Company):\n", 995 | " def fun2(self):\n", 996 | " print(\"Inside child class.\")\n", 997 | "\n", 998 | "class Player:\n", 999 | " def fun3(self):\n", 1000 | " print(\"Inside Player class.\")\n", 1001 | "\n", 1002 | "# Result True\n", 1003 | "print(issubclass(Employee, Company))\n", 1004 | "\n", 1005 | "# Result False\n", 1006 | "print(issubclass(Employee, list))\n", 1007 | "\n", 1008 | "# Result False\n", 1009 | "print(issubclass(Player, Company))\n", 1010 | "\n", 1011 | "# Result True\n", 1012 | "print(issubclass(Employee, (list, Company)))\n", 1013 | "\n", 1014 | "# Result True\n", 1015 | "print(issubclass(Company, (list, Company)))" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "markdown", 1020 | "metadata": {}, 1021 | "source": [ 1022 | "## Method Overriding\n", 1023 | "\n", 1024 | "In inheritance, all members available in the parent class are by default available in the child class. If the child class does not satisfy with parent class implementation, then the child class is allowed to redefine that method by extending additional functions in the child class. This concept is called **method overriding**.\n", 1025 | "\n", 1026 | "When a child class method has the same name, same parameters, and same return type as a method in its superclass, then the method in the child is said to **override** the method in the parent class.\n", 1027 | "\n", 1028 | "
\n", 1029 | "\n", 1030 | "
" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "code", 1035 | "execution_count": 19, 1036 | "metadata": { 1037 | "ExecuteTime": { 1038 | "end_time": "2021-06-20T19:11:20.977224Z", 1039 | "start_time": "2021-06-20T19:11:20.958672Z" 1040 | } 1041 | }, 1042 | "outputs": [ 1043 | { 1044 | "name": "stdout", 1045 | "output_type": "stream", 1046 | "text": [ 1047 | "max speed is 200 Km/Hour\n" 1048 | ] 1049 | } 1050 | ], 1051 | "source": [ 1052 | "# Example 1:\n", 1053 | "\n", 1054 | "class Vehicle:\n", 1055 | " def max_speed(self):\n", 1056 | " print(\"max speed is 100 Km/Hour\")\n", 1057 | "\n", 1058 | "class Car(Vehicle):\n", 1059 | " # overridden the implementation of Vehicle class\n", 1060 | " def max_speed(self):\n", 1061 | " print(\"max speed is 200 Km/Hour\")\n", 1062 | "\n", 1063 | "# Creating object of Car class\n", 1064 | "car = Car()\n", 1065 | "car.max_speed()" 1066 | ] 1067 | }, 1068 | { 1069 | "cell_type": "markdown", 1070 | "metadata": {}, 1071 | "source": [ 1072 | "**Explanation:**\n", 1073 | "\n", 1074 | "In the above example, we create two classes named **`Vehicle`** (Parent class) and **`Car`** (Child class). The class **`Car`** extends from the class **`Vehicle`** so, all properties of the parent class are available in the child class. In addition to that, the child class redefined the method **`max_speed()`**." 1075 | ] 1076 | }, 1077 | { 1078 | "cell_type": "code", 1079 | "execution_count": 20, 1080 | "metadata": { 1081 | "ExecuteTime": { 1082 | "end_time": "2021-06-20T19:11:23.111973Z", 1083 | "start_time": "2021-06-20T19:11:23.103186Z" 1084 | } 1085 | }, 1086 | "outputs": [ 1087 | { 1088 | "name": "stdout", 1089 | "output_type": "stream", 1090 | "text": [ 1091 | "Calling child method\n" 1092 | ] 1093 | } 1094 | ], 1095 | "source": [ 1096 | "# Example 2:\n", 1097 | "\n", 1098 | "class Parent: # define parent class\n", 1099 | " def myMethod(self):\n", 1100 | " print ('Calling parent method')\n", 1101 | "\n", 1102 | "\n", 1103 | "class Child(Parent): # define child class\n", 1104 | " def myMethod(self):\n", 1105 | " print ('Calling child method')\n", 1106 | "\n", 1107 | "\n", 1108 | "c = Child() # instance of child\n", 1109 | "c.myMethod() # child calls overridden method" 1110 | ] 1111 | }, 1112 | { 1113 | "cell_type": "code", 1114 | "execution_count": 21, 1115 | "metadata": { 1116 | "ExecuteTime": { 1117 | "end_time": "2021-06-20T19:11:24.170070Z", 1118 | "start_time": "2021-06-20T19:11:24.133941Z" 1119 | } 1120 | }, 1121 | "outputs": [ 1122 | { 1123 | "name": "stdout", 1124 | "output_type": "stream", 1125 | "text": [ 1126 | "Bird is ready\n", 1127 | "Penguin is ready\n", 1128 | "Penguin\n", 1129 | "Swim faster\n", 1130 | "Run faster\n" 1131 | ] 1132 | }, 1133 | { 1134 | "data": { 1135 | "text/plain": [ 1136 | "True" 1137 | ] 1138 | }, 1139 | "execution_count": 21, 1140 | "metadata": {}, 1141 | "output_type": "execute_result" 1142 | } 1143 | ], 1144 | "source": [ 1145 | "# Example 3:\n", 1146 | "\n", 1147 | "# parent class\n", 1148 | "class Bird:\n", 1149 | " \n", 1150 | " def __init__(self):\n", 1151 | " print(\"Bird is ready\")\n", 1152 | "\n", 1153 | " def whoisThis(self):\n", 1154 | " print(\"Bird\")\n", 1155 | "\n", 1156 | " def swim(self):\n", 1157 | " print(\"Swim faster\")\n", 1158 | "\n", 1159 | "# child class\n", 1160 | "class Penguin(Bird):\n", 1161 | "\n", 1162 | " def __init__(self):\n", 1163 | " # call super() function to run the __init__() method of the parent class inside the child class.\n", 1164 | " super().__init__()\n", 1165 | " print(\"Penguin is ready\")\n", 1166 | "\n", 1167 | " def whoisThis(self):\n", 1168 | " print(\"Penguin\")\n", 1169 | "\n", 1170 | " def run(self):\n", 1171 | " print(\"Run faster\")\n", 1172 | "\n", 1173 | "peggy = Penguin()\n", 1174 | "peggy.whoisThis()\n", 1175 | "peggy.swim()\n", 1176 | "peggy.run()\n", 1177 | "\n", 1178 | "#issubclass(Penguin, Bird) \n", 1179 | "isinstance(peggy, Bird)" 1180 | ] 1181 | }, 1182 | { 1183 | "cell_type": "markdown", 1184 | "metadata": {}, 1185 | "source": [ 1186 | "## Example of Method Overriding in Python\n", 1187 | "\n", 1188 | "In the example of **\"Polygon\"** and **\"Triangle\"**, notice that **`__init__()`** method was defined in both classes, **`Triangle`** as well **`Polygon`**. When this happens, the method in the derived class overrides that in the base class. This is to say, **`__init__()`** in **`Triangle`** gets preference over the **`__init__`** in **`Polygon`**.\n", 1189 | "\n", 1190 | "Generally when overriding a base method, we tend to extend the definition rather than simply replace it. The same is being done by calling the method in base class from the one in derived class (calling **`Polygon.__init__()`** from **`__init__()`** in **`Triangle`**)." 1191 | ] 1192 | }, 1193 | { 1194 | "cell_type": "markdown", 1195 | "metadata": {}, 1196 | "source": [ 1197 | "A better option would be to use the built-in function **`super()`**. So, **`super().__init__(3)`** is equivalent to **`Polygon.__init__(self,3)`** and is preferred. To learn more about the **`super()`** function in Python, visit **[Python super() function](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)**." 1198 | ] 1199 | }, 1200 | { 1201 | "cell_type": "markdown", 1202 | "metadata": {}, 1203 | "source": [ 1204 | "Two built-in functions **`isinstance()`** and **`issubclass()`** are used to check inheritances.\n", 1205 | "\n", 1206 | "The function **`isinstance()`** returns **`True`** if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class object." 1207 | ] 1208 | }, 1209 | { 1210 | "cell_type": "code", 1211 | "execution_count": 22, 1212 | "metadata": { 1213 | "ExecuteTime": { 1214 | "end_time": "2021-06-20T19:11:30.224709Z", 1215 | "start_time": "2021-06-20T19:11:30.207135Z" 1216 | } 1217 | }, 1218 | "outputs": [ 1219 | { 1220 | "data": { 1221 | "text/plain": [ 1222 | "True" 1223 | ] 1224 | }, 1225 | "execution_count": 22, 1226 | "metadata": {}, 1227 | "output_type": "execute_result" 1228 | } 1229 | ], 1230 | "source": [ 1231 | "isinstance(t,Triangle)" 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "code", 1236 | "execution_count": 23, 1237 | "metadata": { 1238 | "ExecuteTime": { 1239 | "end_time": "2021-06-20T19:11:31.070407Z", 1240 | "start_time": "2021-06-20T19:11:31.062598Z" 1241 | } 1242 | }, 1243 | "outputs": [ 1244 | { 1245 | "data": { 1246 | "text/plain": [ 1247 | "True" 1248 | ] 1249 | }, 1250 | "execution_count": 23, 1251 | "metadata": {}, 1252 | "output_type": "execute_result" 1253 | } 1254 | ], 1255 | "source": [ 1256 | "isinstance(t,Polygon)" 1257 | ] 1258 | }, 1259 | { 1260 | "cell_type": "code", 1261 | "execution_count": 24, 1262 | "metadata": { 1263 | "ExecuteTime": { 1264 | "end_time": "2021-06-20T19:11:31.733488Z", 1265 | "start_time": "2021-06-20T19:11:31.719820Z" 1266 | } 1267 | }, 1268 | "outputs": [ 1269 | { 1270 | "data": { 1271 | "text/plain": [ 1272 | "False" 1273 | ] 1274 | }, 1275 | "execution_count": 24, 1276 | "metadata": {}, 1277 | "output_type": "execute_result" 1278 | } 1279 | ], 1280 | "source": [ 1281 | "isinstance(t,int)" 1282 | ] 1283 | }, 1284 | { 1285 | "cell_type": "code", 1286 | "execution_count": 25, 1287 | "metadata": { 1288 | "ExecuteTime": { 1289 | "end_time": "2021-06-20T19:11:32.469320Z", 1290 | "start_time": "2021-06-20T19:11:32.448815Z" 1291 | } 1292 | }, 1293 | "outputs": [ 1294 | { 1295 | "data": { 1296 | "text/plain": [ 1297 | "True" 1298 | ] 1299 | }, 1300 | "execution_count": 25, 1301 | "metadata": {}, 1302 | "output_type": "execute_result" 1303 | } 1304 | ], 1305 | "source": [ 1306 | "isinstance(t,object)" 1307 | ] 1308 | }, 1309 | { 1310 | "cell_type": "markdown", 1311 | "metadata": {}, 1312 | "source": [ 1313 | "Similarly, **`issubclass()`** is used to check for class inheritance." 1314 | ] 1315 | }, 1316 | { 1317 | "cell_type": "code", 1318 | "execution_count": 26, 1319 | "metadata": { 1320 | "ExecuteTime": { 1321 | "end_time": "2021-06-20T19:11:33.715406Z", 1322 | "start_time": "2021-06-20T19:11:33.703687Z" 1323 | } 1324 | }, 1325 | "outputs": [ 1326 | { 1327 | "data": { 1328 | "text/plain": [ 1329 | "False" 1330 | ] 1331 | }, 1332 | "execution_count": 26, 1333 | "metadata": {}, 1334 | "output_type": "execute_result" 1335 | } 1336 | ], 1337 | "source": [ 1338 | "issubclass(Polygon,Triangle)" 1339 | ] 1340 | }, 1341 | { 1342 | "cell_type": "code", 1343 | "execution_count": 27, 1344 | "metadata": { 1345 | "ExecuteTime": { 1346 | "end_time": "2021-06-20T19:11:34.378489Z", 1347 | "start_time": "2021-06-20T19:11:34.370676Z" 1348 | } 1349 | }, 1350 | "outputs": [ 1351 | { 1352 | "data": { 1353 | "text/plain": [ 1354 | "True" 1355 | ] 1356 | }, 1357 | "execution_count": 27, 1358 | "metadata": {}, 1359 | "output_type": "execute_result" 1360 | } 1361 | ], 1362 | "source": [ 1363 | "issubclass(Triangle,Polygon)" 1364 | ] 1365 | }, 1366 | { 1367 | "cell_type": "code", 1368 | "execution_count": 28, 1369 | "metadata": { 1370 | "ExecuteTime": { 1371 | "end_time": "2021-06-20T19:11:35.701719Z", 1372 | "start_time": "2021-06-20T19:11:35.688047Z" 1373 | }, 1374 | "scrolled": true 1375 | }, 1376 | "outputs": [ 1377 | { 1378 | "data": { 1379 | "text/plain": [ 1380 | "True" 1381 | ] 1382 | }, 1383 | "execution_count": 28, 1384 | "metadata": {}, 1385 | "output_type": "execute_result" 1386 | } 1387 | ], 1388 | "source": [ 1389 | "issubclass(bool,int)" 1390 | ] 1391 | }, 1392 | { 1393 | "cell_type": "markdown", 1394 | "metadata": {}, 1395 | "source": [ 1396 | "Also, see **[Python isinstance()](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/034_Python_isinstance%28%29.ipynb)**." 1397 | ] 1398 | }, 1399 | { 1400 | "cell_type": "markdown", 1401 | "metadata": {}, 1402 | "source": [ 1403 | "### Method Resolution Order in Python\n", 1404 | "\n", 1405 | "In Python, Method Resolution Order(MRO) is the order by which **Python looks for a method or attribute**. First, the method or attribute is searched within a class, and then it follows the order we specified while inheriting.\n", 1406 | "\n", 1407 | "This order is also called the Linearization of a class, and a set of rules is called MRO (Method Resolution Order). The **MRO plays an essential role in multiple inheritances as a single method may found in multiple parent classes**.\n", 1408 | "\n", 1409 | "In multiple inheritance, the following search order is followed.\n", 1410 | "\n", 1411 | "1. First, it searches in the current parent class if not available, then searches in the parents class specified while inheriting (that is left to right.)\n", 1412 | "2. We can get the MRO of a class. For this purpose, we can use either the **`mro`** attribute or the **`mro()`** method." 1413 | ] 1414 | }, 1415 | { 1416 | "cell_type": "markdown", 1417 | "metadata": {}, 1418 | "source": [ 1419 | "**Example:**\n", 1420 | "\n", 1421 | "```python\n", 1422 | "class A:\n", 1423 | " def process(self):\n", 1424 | " print(\" In class A\")\n", 1425 | "\n", 1426 | "class B(A):\n", 1427 | " def process(self):\n", 1428 | " print(\" In class B\")\n", 1429 | "\n", 1430 | "class C(B, A):\n", 1431 | " def process(self):\n", 1432 | " print(\" In class C\")\n", 1433 | "\n", 1434 | "# Creating object of C class\n", 1435 | "C1 = C()\n", 1436 | "C1.process()\n", 1437 | "print(C.mro())\n", 1438 | "# In class C\n", 1439 | "# [, , , ]\n", 1440 | "```" 1441 | ] 1442 | }, 1443 | { 1444 | "cell_type": "markdown", 1445 | "metadata": {}, 1446 | "source": [ 1447 | "**Explanation:**\n", 1448 | "\n", 1449 | "In the above example, we create three classes named **`A`**, **`B`** and **`C`**. Class **`B`** is inherited from **`A`**, class **`C`** inherits from **`B`** and **`A`**. When we create an object of the **`C`** class and calling the **`process()`** method, Python looks for the **`process()`** method in the current class in the **`C`** class itself.\n", 1450 | "\n", 1451 | "Then search for parent classes, namely **`B`** and **`A`**, because **`C`** class inherit from **`B`** and **`A`**. that is, **`C(B, A)`** and always search in **left to right manner**." 1452 | ] 1453 | }, 1454 | { 1455 | "cell_type": "code", 1456 | "execution_count": 29, 1457 | "metadata": { 1458 | "ExecuteTime": { 1459 | "end_time": "2021-06-20T19:11:44.127920Z", 1460 | "start_time": "2021-06-20T19:11:44.120109Z" 1461 | } 1462 | }, 1463 | "outputs": [ 1464 | { 1465 | "name": "stdout", 1466 | "output_type": "stream", 1467 | "text": [ 1468 | "True\n" 1469 | ] 1470 | } 1471 | ], 1472 | "source": [ 1473 | "# Output: True\n", 1474 | "print(issubclass(list,object))" 1475 | ] 1476 | }, 1477 | { 1478 | "cell_type": "code", 1479 | "execution_count": 30, 1480 | "metadata": { 1481 | "ExecuteTime": { 1482 | "end_time": "2021-06-20T19:11:44.957013Z", 1483 | "start_time": "2021-06-20T19:11:44.943347Z" 1484 | } 1485 | }, 1486 | "outputs": [ 1487 | { 1488 | "name": "stdout", 1489 | "output_type": "stream", 1490 | "text": [ 1491 | "True\n" 1492 | ] 1493 | } 1494 | ], 1495 | "source": [ 1496 | "# Output: True\n", 1497 | "print(isinstance(5.5,object))" 1498 | ] 1499 | }, 1500 | { 1501 | "cell_type": "code", 1502 | "execution_count": 31, 1503 | "metadata": { 1504 | "ExecuteTime": { 1505 | "end_time": "2021-06-20T19:11:45.725565Z", 1506 | "start_time": "2021-06-20T19:11:45.718729Z" 1507 | } 1508 | }, 1509 | "outputs": [ 1510 | { 1511 | "name": "stdout", 1512 | "output_type": "stream", 1513 | "text": [ 1514 | "True\n" 1515 | ] 1516 | } 1517 | ], 1518 | "source": [ 1519 | "# Output: True\n", 1520 | "print(isinstance(\"Hello\",object))" 1521 | ] 1522 | }, 1523 | { 1524 | "cell_type": "markdown", 1525 | "metadata": {}, 1526 | "source": [ 1527 | "In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching the same class twice.\n", 1528 | "\n", 1529 | "So, in the above example of **`MultiDerived`** class the search order is [**`MultiDerived`**, **`Base1`**, **`Base2`**, **`object`**]. This order is also called linearization of **`MultiDerived`** class and the set of rules used to find this order is called **Method Resolution Order (MRO)**.\n", 1530 | "\n", 1531 | "MRO must prevent local precedence ordering and also provide monotonicity. It ensures that a class always appears before its parents. In case of multiple parents, the order is the same as tuples of base classes.\n", 1532 | "\n", 1533 | "MRO of a class can be viewed as the **`__mro__`** attribute or the **`mro()`** method. The former returns a tuple while the latter returns a list\n", 1534 | "\n", 1535 | "```python\n", 1536 | ">>> MultiDerived.__mro__\n", 1537 | "(,\n", 1538 | " ,\n", 1539 | " ,\n", 1540 | " )\n", 1541 | "\n", 1542 | ">>> MultiDerived.mro()\n", 1543 | "[,\n", 1544 | " ,\n", 1545 | " ,\n", 1546 | " ]\n", 1547 | "```" 1548 | ] 1549 | }, 1550 | { 1551 | "cell_type": "markdown", 1552 | "metadata": {}, 1553 | "source": [ 1554 | "Here is a little more complex multiple inheritance example and its visualization along with the MRO.\n", 1555 | "\n", 1556 | "
\n", 1557 | "\n", 1558 | "
" 1559 | ] 1560 | }, 1561 | { 1562 | "cell_type": "code", 1563 | "execution_count": 32, 1564 | "metadata": { 1565 | "ExecuteTime": { 1566 | "end_time": "2021-06-20T19:11:48.932575Z", 1567 | "start_time": "2021-06-20T19:11:48.919877Z" 1568 | }, 1569 | "scrolled": true 1570 | }, 1571 | "outputs": [ 1572 | { 1573 | "name": "stdout", 1574 | "output_type": "stream", 1575 | "text": [ 1576 | "[, , , , , , ]\n" 1577 | ] 1578 | } 1579 | ], 1580 | "source": [ 1581 | "# Demonstration of MRO\n", 1582 | "\n", 1583 | "class X:\n", 1584 | " pass\n", 1585 | "\n", 1586 | "\n", 1587 | "class Y:\n", 1588 | " pass\n", 1589 | "\n", 1590 | "\n", 1591 | "class Z:\n", 1592 | " pass\n", 1593 | "\n", 1594 | "\n", 1595 | "class A(X, Y):\n", 1596 | " pass\n", 1597 | "\n", 1598 | "\n", 1599 | "class B(Y, Z):\n", 1600 | " pass\n", 1601 | "\n", 1602 | "\n", 1603 | "class M(B, A, Z):\n", 1604 | " pass\n", 1605 | "\n", 1606 | "# Output:\n", 1607 | "# [, ,\n", 1608 | "# , ,\n", 1609 | "# , ,\n", 1610 | "# ]\n", 1611 | "\n", 1612 | "print(M.mro())" 1613 | ] 1614 | }, 1615 | { 1616 | "cell_type": "markdown", 1617 | "metadata": {}, 1618 | "source": [ 1619 | "To know the actual algorithm on how MRO is calculated, visit [**Discussion on MRO**](https://www.python.org/download/releases/2.3/mro/)." 1620 | ] 1621 | }, 1622 | { 1623 | "cell_type": "code", 1624 | "execution_count": null, 1625 | "metadata": {}, 1626 | "outputs": [], 1627 | "source": [] 1628 | } 1629 | ], 1630 | "metadata": { 1631 | "hide_input": false, 1632 | "kernelspec": { 1633 | "display_name": "Python 3", 1634 | "language": "python", 1635 | "name": "python3" 1636 | }, 1637 | "language_info": { 1638 | "codemirror_mode": { 1639 | "name": "ipython", 1640 | "version": 3 1641 | }, 1642 | "file_extension": ".py", 1643 | "mimetype": "text/x-python", 1644 | "name": "python", 1645 | "nbconvert_exporter": "python", 1646 | "pygments_lexer": "ipython3", 1647 | "version": "3.8.8" 1648 | }, 1649 | "toc": { 1650 | "base_numbering": 1, 1651 | "nav_menu": {}, 1652 | "number_sections": true, 1653 | "sideBar": true, 1654 | "skip_h1_title": false, 1655 | "title_cell": "Table of Contents", 1656 | "title_sidebar": "Contents", 1657 | "toc_cell": false, 1658 | "toc_position": {}, 1659 | "toc_section_display": true, 1660 | "toc_window_display": false 1661 | }, 1662 | "varInspector": { 1663 | "cols": { 1664 | "lenName": 16, 1665 | "lenType": 16, 1666 | "lenVar": 40 1667 | }, 1668 | "kernels_config": { 1669 | "python": { 1670 | "delete_cmd_postfix": "", 1671 | "delete_cmd_prefix": "del ", 1672 | "library": "var_list.py", 1673 | "varRefreshCmd": "print(var_dic_list())" 1674 | }, 1675 | "r": { 1676 | "delete_cmd_postfix": ") ", 1677 | "delete_cmd_prefix": "rm(", 1678 | "library": "var_list.r", 1679 | "varRefreshCmd": "cat(var_dic_list()) " 1680 | } 1681 | }, 1682 | "types_to_exclude": [ 1683 | "module", 1684 | "function", 1685 | "builtin_function_or_method", 1686 | "instance", 1687 | "_Feature" 1688 | ], 1689 | "window_display": false 1690 | } 1691 | }, 1692 | "nbformat": 4, 1693 | "nbformat_minor": 2 1694 | } 1695 | -------------------------------------------------------------------------------- /004_Python_Operator_Overloading.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 | "# Python Operator Overloading\n", 17 | "\n", 18 | "You can change the meaning of an operator in Python depending upon the operands used. In this tutorial, you will learn how to use operator overloading in Python Object Oriented Programming." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Python Operator Overloading\n", 26 | "\n", 27 | "[**Python operators**](https://github.com/milaan9/01_Python_Introduction/blob/main/012_Python_Operators.ipynb) work for built-in classes. But the same operator behaves differently with different types. For example, the **`+`** operator will perform arithmetic addition on two numbers, merge two lists, or concatenate two strings.\n", 28 | "\n", 29 | "This feature in Python that allows the same operator to have different meaning according to the context is called **operator overloading**.\n", 30 | "\n", 31 | "Python Operator overloading allows us to use mathematical, logical, and bitwise operators on Python objects.\n", 32 | "\n", 33 | "So what happens when we use them with objects of a user-defined class? Let us consider the following class, which tries to simulate a point in 2-D coordinate system." 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": { 40 | "ExecuteTime": { 41 | "end_time": "2021-06-20T19:11:59.737171Z", 42 | "start_time": "2021-06-20T19:11:59.424678Z" 43 | }, 44 | "scrolled": true 45 | }, 46 | "outputs": [ 47 | { 48 | "ename": "TypeError", 49 | "evalue": "unsupported operand type(s) for +: 'Point' and 'Point'", 50 | "output_type": "error", 51 | "traceback": [ 52 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 53 | "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", 54 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[0mp1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[0mp2\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mp1\u001b[0m\u001b[1;33m+\u001b[0m\u001b[0mp2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 55 | "\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'Point' and 'Point'" 56 | ] 57 | } 58 | ], 59 | "source": [ 60 | "# Example 1: error\n", 61 | "\n", 62 | "class Point:\n", 63 | " def __init__(self, x=0, y=0):\n", 64 | " self.x = x\n", 65 | " self.y = y\n", 66 | "\n", 67 | "p1 = Point(1, 2)\n", 68 | "p2 = Point(2, 3)\n", 69 | "print(p1+p2)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "**Explanation:**\n", 77 | "\n", 78 | "Here, we can see that a **`TypeError`** was raised, since Python didn't know how to add two **`Point`** objects together.\n", 79 | "\n", 80 | "However, we can achieve this task in Python through operator overloading. But first, let's get a notion about special functions." 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "## Python Special Functions\n", 88 | "\n", 89 | "Class functions that begin with double underscore **`__`** are called special functions in Python.\n", 90 | "\n", 91 | "These functions are not the typical functions that we define for a class. The **`__init__()`** function we defined above is one of them. It gets called every time we create a new object of that class.\n", 92 | "\n", 93 | "There are numerous other special functions in Python. Visit [**Python Special Functions**](https://docs.python.org/3/reference/datamodel.html#special-method-names) to learn more about them.\n", 94 | "\n", 95 | "Using special functions, we can make our class compatible with built-in functions." 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 2, 101 | "metadata": { 102 | "ExecuteTime": { 103 | "end_time": "2021-06-20T19:12:01.734230Z", 104 | "start_time": "2021-06-20T19:12:01.726418Z" 105 | } 106 | }, 107 | "outputs": [ 108 | { 109 | "name": "stdout", 110 | "output_type": "stream", 111 | "text": [ 112 | "<__main__.Point object at 0x00000218FFC65220>\n" 113 | ] 114 | } 115 | ], 116 | "source": [ 117 | "p1 = Point(2,3)\n", 118 | "print(p1)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "Suppose we want the **`print()`** function to print the coordinates of the **`Point`** object instead of what we got. We can define a **`__str__()`** method in our class that controls how the object gets printed. Let's look at how we can achieve this:\n", 126 | "\n", 127 | "```python\n", 128 | "class Point:\n", 129 | " def __init__(self, x = 0, y = 0):\n", 130 | " self.x = x\n", 131 | " self.y = y\n", 132 | " \n", 133 | " def __str__(self):\n", 134 | " return \"({0},{1})\".format(self.x,self.y)\n", 135 | "```" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "Now let's try the **`print()`** function again." 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 3, 148 | "metadata": { 149 | "ExecuteTime": { 150 | "end_time": "2021-06-20T19:12:03.562338Z", 151 | "start_time": "2021-06-20T19:12:03.543787Z" 152 | } 153 | }, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "(2, 3)\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "# Example 1: without error\n", 165 | "\n", 166 | "class Point:\n", 167 | " def __init__(self, x=0, y=0):\n", 168 | " self.x = x\n", 169 | " self.y = y\n", 170 | "\n", 171 | " def __str__(self):\n", 172 | " return \"({0}, {1})\".format(self.x, self.y)\n", 173 | "\n", 174 | "\n", 175 | "p1 = Point(2, 3)\n", 176 | "print(p1)" 177 | ] 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "metadata": {}, 182 | "source": [ 183 | "That's better. Turns out, that this same method is invoked when we use the built-in function **`str()`** or **`format()`**." 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": 4, 189 | "metadata": { 190 | "ExecuteTime": { 191 | "end_time": "2021-06-20T19:12:04.898264Z", 192 | "start_time": "2021-06-20T19:12:04.869948Z" 193 | } 194 | }, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "'(2, 3)'" 200 | ] 201 | }, 202 | "execution_count": 4, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "str(p1)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 5, 214 | "metadata": { 215 | "ExecuteTime": { 216 | "end_time": "2021-06-20T19:12:05.529122Z", 217 | "start_time": "2021-06-20T19:12:05.518382Z" 218 | } 219 | }, 220 | "outputs": [ 221 | { 222 | "data": { 223 | "text/plain": [ 224 | "'(2, 3)'" 225 | ] 226 | }, 227 | "execution_count": 5, 228 | "metadata": {}, 229 | "output_type": "execute_result" 230 | } 231 | ], 232 | "source": [ 233 | "format(p1)" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "So, when you use **`str(p1)`** or **`format(p1)`**, Python internally calls the **`p1.__str__()`** method. Hence the name, special functions.\n", 241 | "\n", 242 | "Now let's go back to operator overloading." 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 6, 248 | "metadata": { 249 | "ExecuteTime": { 250 | "end_time": "2021-06-20T19:12:06.713683Z", 251 | "start_time": "2021-06-20T19:12:06.694154Z" 252 | }, 253 | "scrolled": true 254 | }, 255 | "outputs": [ 256 | { 257 | "name": "stdout", 258 | "output_type": "stream", 259 | "text": [ 260 | "Total Number of pages: 250\n" 261 | ] 262 | } 263 | ], 264 | "source": [ 265 | "# Example 2:\n", 266 | "\n", 267 | "class Book:\n", 268 | " def __init__(self, pages):\n", 269 | " self.pages = pages\n", 270 | "\n", 271 | " def __add__(self, other):\n", 272 | " return self.pages + other.pages\n", 273 | "\n", 274 | "b1 = Book(150)\n", 275 | "b2 = Book(100)\n", 276 | "print(\"Total Number of pages:\", b1 + b2)\n", 277 | "# Output Total Number of pages: 250" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "**Explanation:**\n", 285 | "\n", 286 | "In the above example, we overload the **`+`** operator internally **`+`** operator implemented using def **`__add__(self, other)`** method. This method is known as a **magic method**." 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 7, 292 | "metadata": { 293 | "ExecuteTime": { 294 | "end_time": "2021-06-20T19:12:08.157522Z", 295 | "start_time": "2021-06-20T19:12:08.142874Z" 296 | } 297 | }, 298 | "outputs": [ 299 | { 300 | "name": "stdout", 301 | "output_type": "stream", 302 | "text": [ 303 | "Vector (7, 8)\n" 304 | ] 305 | } 306 | ], 307 | "source": [ 308 | "# Example 3:\n", 309 | "\n", 310 | "class Vector:\n", 311 | " def __init__(self, a, b):\n", 312 | " self.a = a\n", 313 | " self.b = b\n", 314 | "\n", 315 | " def __str__(self):\n", 316 | " return 'Vector (%d, %d)' % (self.a, self.b)\n", 317 | "\n", 318 | " def __add__(self,other):\n", 319 | " return Vector(self.a + other.a, self.b + other.b)\n", 320 | "\n", 321 | "v1 = Vector(2,10)\n", 322 | "v2 = Vector(5,-2)\n", 323 | "print (v1 + v2)" 324 | ] 325 | }, 326 | { 327 | "cell_type": "markdown", 328 | "metadata": {}, 329 | "source": [ 330 | "## Overloading the `+` Operator\n", 331 | "\n", 332 | "To overload the **`+`** operator, we will need to implement **`__add__()`** function in the class. With great power comes great responsibility. We can do whatever we like, inside this function. But it is more sensible to return a **`Point`** object of the coordinate sum.\n", 333 | "\n", 334 | "```python\n", 335 | ">>> class Point:\n", 336 | ">>> def __init__(self, x=0, y=0):\n", 337 | ">>> self.x = x\n", 338 | ">>> self.y = y\n", 339 | "\n", 340 | ">>> def __str__(self):\n", 341 | ">>> return \"({0},{1})\".format(self.x, self.y)\n", 342 | "\n", 343 | ">>> def __add__(self, other):\n", 344 | ">>> x = self.x + other.x\n", 345 | ">>> y = self.y + other.y\n", 346 | ">>> return Point(x, y)\n", 347 | "```" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "Now let's try the addition operation again:" 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": 8, 360 | "metadata": { 361 | "ExecuteTime": { 362 | "end_time": "2021-06-20T19:12:10.720975Z", 363 | "start_time": "2021-06-20T19:12:10.702424Z" 364 | } 365 | }, 366 | "outputs": [ 367 | { 368 | "name": "stdout", 369 | "output_type": "stream", 370 | "text": [ 371 | "(3,5)\n" 372 | ] 373 | } 374 | ], 375 | "source": [ 376 | "# Example 1:\n", 377 | "\n", 378 | "class Point:\n", 379 | " def __init__(self, x=0, y=0):\n", 380 | " self.x = x\n", 381 | " self.y = y\n", 382 | "\n", 383 | " def __str__(self):\n", 384 | " return \"({0},{1})\".format(self.x, self.y)\n", 385 | "\n", 386 | " def __add__(self, other):\n", 387 | " x = self.x + other.x\n", 388 | " y = self.y + other.y\n", 389 | " return Point(x, y)\n", 390 | "\n", 391 | "\n", 392 | "p1 = Point(1, 2)\n", 393 | "p2 = Point(2, 3)\n", 394 | "\n", 395 | "print(p1+p2)" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "**Explanation:**\n", 403 | "\n", 404 | "What actually happens is that, when you use **`p1 + p2`**, Python calls **`p1.__add__(p2)`** which in turn is **`Point.__add__(p1,p2)`**. After this, the addition operation is carried out the way we specified.\n", 405 | "\n", 406 | "In Python, there are different **magic methods** available to perform **overloading operations**. The below table shows the magic methods’ names to overload the mathematical operator, assignment operator, and relational operators in Python." 407 | ] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "metadata": {}, 412 | "source": [ 413 | "| Operator | Expression | Internally | Magic method | \n", 414 | "|:----:| :---: |:---: |:---: |\n", 415 | "| **Addition** | **`p1 + p2`** | **`p1.__add__(p2)`** | **`__add__(self, other)`** |\n", 416 | "| **Subtraction** | **`p1 - p2`** | **`p1.__sub__(p2)`** | **`__sub__(self, other)`** |\n", 417 | "| **Multiplication** | **`p1 * p2`** | **`p1.__mul__(p2)`** | **`\t__mul__(self, other)`** |\n", 418 | "| **Power** | **`p1 ** p2`** | **`p1.__pow__(p2)`** | **`__pow__(self, other)`** |\n", 419 | "| **Increment** | **`p1 += p2`** | **`p1.__iadd__(p2)`** | **`__iadd__(self, other)`** |\n", 420 | "| **Decrement** | **`p1 -= p2`** | **`p1.__isub__(p2)`** | **`__isub__(self, other)`** |\n", 421 | "| **Product** | **`p1 *= p2`** | **`p1.__imul__(p2)`** | **`__imul__(self, other)`** |\n", 422 | "| **Division** | **`p1 /= p2`** | **`p1.__idiv__(p2)`** | **`__idiv__(self, other)`** |\n", 423 | "| **Modulus** | **`p1 %= p2`** | **`p1.__imod__(p2)`** | **`__imod__(self, other)`** |\n", 424 | "| **Power** | **`p1 **= p2`** | **`p1.__ipow__(p2)`** | **`__ipow__(self, other)`** |\n", 425 | "| **Division** | **`p1 / p2`** | **`p1.__truediv__(p2)`** | **`__div__(self, other)`** |\n", 426 | "| **Floor Division** | **`p1 // p2`** | **`p1.__floordiv__(p2)`** | **`__floordiv__(self,other)`** |\n", 427 | "| **Remainder (modulo)** | **`p1 % p2`** | **`p1.__mod__(p2)`** | **`__mod__(self, other)`** |\n", 428 | "| **Bitwise Left Shift** | **`p1 << p2`** | **`p1.__lshift__(p2)`** | **`__lshift__(self, other)`** |\n", 429 | "| **Bitwise Right Shift** | **`p1 >> p2`** | **`p1.__rshift__(p2)`** | **`__rshift__(self, other)`** |\n", 430 | "| **Bitwise AND** | **`p1 & p2`** | **`p1.__and__(p2)`** | **`__and__(self, other)`** |\n", 431 | "| **Bitwise OR** | **`p1 I p2`** | **`p1.__or__(p2)`** | **`__or__(self, other)`** |\n", 432 | "| **Bitwise XOR** | **`p1 ^ p2`** | **`p1.__xor__(p2)`** | **`__xor__(self, other)`** |\n", 433 | "| **Bitwise NOT** | **`~p1`** | **`p1.__invert__()`** | **`__invert__(self)`** |" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "## Overloading Comparison Operators\n", 441 | "\n", 442 | "Python does not limit operator overloading to arithmetic operators only. We can overload comparison operators as well.\n", 443 | "\n", 444 | "Suppose we wanted to implement the less than symbol **`<`** symbol in our **`Point`** class.\n", 445 | "\n", 446 | "Let us compare the magnitude of these points from the origin and return the result for this purpose. It can be implemented as follows." 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": 9, 452 | "metadata": { 453 | "ExecuteTime": { 454 | "end_time": "2021-06-20T19:12:13.606695Z", 455 | "start_time": "2021-06-20T19:12:13.591076Z" 456 | } 457 | }, 458 | "outputs": [ 459 | { 460 | "name": "stdout", 461 | "output_type": "stream", 462 | "text": [ 463 | "True\n", 464 | "False\n", 465 | "False\n" 466 | ] 467 | } 468 | ], 469 | "source": [ 470 | "# Example 1: overloading the less than operator\n", 471 | "class Point:\n", 472 | " def __init__(self, x=0, y=0):\n", 473 | " self.x = x\n", 474 | " self.y = y\n", 475 | "\n", 476 | " def __str__(self):\n", 477 | " return \"({0},{1})\".format(self.x, self.y)\n", 478 | "\n", 479 | " def __lt__(self, other):\n", 480 | " self_mag = (self.x ** 2) + (self.y ** 2)\n", 481 | " other_mag = (other.x ** 2) + (other.y ** 2)\n", 482 | " return self_mag < other_mag\n", 483 | "\n", 484 | "p1 = Point(1,1)\n", 485 | "p2 = Point(-2,-3)\n", 486 | "p3 = Point(1,-1)\n", 487 | "\n", 488 | "# use less than\n", 489 | "print(p1 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 | Last Commit 3 | 4 | 5 | 6 | 7 | Stars Badge 8 | Forks Badge 9 | Size 10 | Pull Requests Badge 11 | Issues Badge 12 | Language 13 | MIT License 14 |

15 | 16 | 17 |

18 | binder 19 | colab 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 ➞ binder 41 | 42 | --- 43 | 44 | ## Frequently asked questions ❔ 45 | 46 | ### How can I thank you for writing and sharing this tutorial? 🌷 47 | 48 | You can Star Badge and Fork Badge 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? GIF 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 | ![Download ZIP](img/dnld_rep.png) 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 | --------------------------------------------------------------------------------