{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Python \n", "\n", "## [Classes](https://docs.python.org/3/tutorial/classes.html) and OOP\n", "\n", "\n", "+ Classes provide means of bundling data and functionality together. \n", "+ Creating a new class is equivalent to creating a new type of object, allowing new instances of that type to be made. \n", "+ Each class instance can have attributes attached to it for maintaining its state. \n", "+ Class instances can also have methods for modifying its state. \n", "\n", "#### The class signature is:\n", "\n", " class ClassName(Superclass - optional):\n", " \n", " .\n", " .\n", " .\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's start with an example" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "class MyClass:\n", " \"\"\"\n", " class documentation string\n", " \"\"\"\n", " \n", " class_attribute1 = 1234\n", " class_attribute2 = 5678\n", " \n", " def class_method(self):\n", " return 'Hello!'" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(type(MyClass))" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "x = MyClass()\n", "y = MyClass()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(type(x))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(type(y))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'class_attribute1',\n", " 'class_attribute2',\n", " 'class_method']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(x)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "8754036884727\n", "8754036884538\n" ] } ], "source": [ "print(x.__hash__())\n", "print(y.__hash__())" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1234" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.class_attribute1" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5678" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.class_attribute2" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Hello!'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.class_method()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "x.class_attribute1 = 4321" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4321" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.class_attribute1" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1234" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.class_attribute1" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "x.new_attribute = \"any new attribute\"" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "z = [1,2,3]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'class_attribute1',\n", " 'class_attribute2',\n", " 'class_method',\n", " 'new_attribute']" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(x)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'class_attribute1',\n", " 'class_attribute2',\n", " 'class_method']" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another example, with __init__() method:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "class MyComplex:\n", " def __init__(self, realpart, imagpart):\n", " self.r = realpart\n", " self.i = imagpart\n", " print(\"__init__ has run\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__init__ has run\n" ] } ], "source": [ "z = MyComplex(2,-4)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__init__ has run\n" ] } ], "source": [ "j = MyComplex(13,12)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z.r" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-4" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z.i" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'i',\n", " 'r']" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(j)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding methods:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", " def __init__(self, width, height):\n", " self.width = width\n", " self.height = height\n", " self.area = width * height\n", " self.perimeter = 2 * (width+height)\n", " if width < height:\n", " self.invert_sides()\n", " \n", " def invert_sides(self):\n", " self.width, self.height = self.height, self.width\n", " print('inverted height and width')" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "my_rectangle = Rectangle(50,10)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "50\n", "10\n" ] } ], "source": [ "print(my_rectangle.width)\n", "print(my_rectangle.height)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "500\n", "120\n" ] } ], "source": [ "print(my_rectangle.area)\n", "print(my_rectangle.perimeter)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "inverted height and width\n" ] } ], "source": [ "my_rectangle.invert_sides()" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10\n", "50\n" ] } ], "source": [ "print(my_rectangle.width)\n", "print(my_rectangle.height)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'area',\n", " 'height',\n", " 'invert_sides',\n", " 'perimeter',\n", " 'width']" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(my_rectangle)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### What is printed when we call the \"print\" primitive in an object?" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'<__main__.Rectangle object at 0x7fc39c6df990>'" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_rectangle.__repr__()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### _print()_ calls the internal __repr__() method." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<__main__.Rectangle object at 0x7f6354250bb0>\n" ] } ], "source": [ "print(my_rectangle)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another class example:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "class Triangle:\n", " def __init__(self, side1, side2, side3):\n", " self.side1 = side1\n", " self.side2 = side2\n", " self.side3 = side3\n", " print('running __init__\\n')\n", " self.type_of_triangle()\n", " \n", " def __repr__(self):\n", " return f\"I am a Triangle with sides {self.side1}, {self.side2} and {self.side3}\"\n", " \n", " #def __repr__(self):\n", " # return \"42\"\n", " \n", " def type_of_triangle(self):\n", " if self.side1 == self.side2 and self.side1 == self.side3:\n", " print('I am equilateral')\n", " self.mytype = 'equilateral'\n", " elif self.side1 == self.side2 or \\\n", " self.side1 == self.side3 or \\\n", " self.side2 == self.side3:\n", " print('I am isosceles')\n", " self.mytype = 'isosceles'\n", " else:\n", " print('I am scalene')\n", " self.mytype = 'scalene'\n", " " ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "running __init__\n", "\n", "I am equilateral\n" ] } ], "source": [ "tri = Triangle(5,5,5)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "running __init__\n", "\n", "I am isosceles\n" ] } ], "source": [ "tri.__init__(5,4,5)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "isosceles\n", "5\n" ] } ], "source": [ "print(tri.mytype)\n", "print(tri.side1)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I am isosceles\n" ] } ], "source": [ "tri.type_of_triangle()" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I am a Triangle with sides 5, 4 and 5\n" ] } ], "source": [ "print(tri)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'mytype',\n", " 'side1',\n", " 'side2',\n", " 'side3',\n", " 'type_of_triangle']" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(tri)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Special Methods for Classes](https://docs.python.org/3/reference/datamodel.html#special-method-names) \n", "\n", "[examples](https://www.pythonlikeyoumeanit.com/Module4_OOP/Special_Methods.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "x[key] = item\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "

Method

Signature

Explanation

Returns string for a printable representation of object

__repr__(self)

repr(x) invokes x.__repr__(), this is also invoked when an object is returned by a console

Returns string representation of an object

__str__(self)

str(x) invokes x.__str__()

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "

Method

Signature

Explanation

Add

__add__(self, other)

x + y invokes x.__add__(y)

Subtract

__sub__(self, other)

x - y invokes x.__sub__(y)

Multiply

__mul__(self, other)

x * y invokes x.__mul__(y)

Divide

__truediv__(self, other)

x / y invokes x.__truediv__(y)

Power

__pow__(self, other)

x ** y invokes x.__pow__(y)

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "

Method

Signature

Explanation

Length

__len__(self)

len(x) invokes x.__len__()

Get Item

__getitem__(self, key)

x[key] invokes x.__getitem__(key)

Set Item

__setitem__(self, key, item)

x[key] = item invokes x.__setitem__(key, item)

Contains

__contains__(self, item)

item in x invokes x.__contains__(item)

Iterator

__iter__(self)

iter(x) invokes x.__iter__()

Next

__next__(self)

next(x) invokes x.__next__()

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operator overloading __gt__ (>) and __lt__ (<)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "class Thing:\n", " def __init__(self, value):\n", " self.__value = value\n", " \n", " def __gt__(self, other):\n", " return self.__value > other.__value\n", " \n", " def __lt__(self, other):\n", " return self.__value < other.__value\n", " \n", "something = Thing(100)\n", "nothing = Thing(10)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "something > nothing" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "something < nothing" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for +: 'Thing' and 'Thing'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msomething\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnothing\u001b[0m \u001b[0;31m# erro\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'Thing' and 'Thing'" ] } ], "source": [ "something + nothing # erro" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "class Thing:\n", " def __init__(self, value):\n", " self.__value = value\n", " \n", " def __gt__(self, other):\n", " return self.__value > other.__value\n", " \n", " def __lt__(self, other):\n", " return self.__value < other.__value\n", " \n", " def __add__(self, other):\n", " return self.__value + other.__value\n", " \n", "something = Thing(100)\n", "nothing = Thing(10)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "110" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "something + nothing" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "110" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nothing + something" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Class inheritance](https://medium.com/swlh/classes-subclasses-in-python-12b6013d9f3)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "class Dog:\n", " def __init__(self, name, race='Royal Street Dog'):\n", " self.name = name\n", " self.race = race\n", " self.tricks = []\n", "\n", " def add_trick(self, trick):\n", " if not trick in self.tricks:\n", " self.tricks.append(trick.title())\n", " else:\n", " print('I have learnt that already!')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "d = Dog('Fido','German Shepherd')\n", "e = Dog('Buddy','Cocker')\n", "f = Dog('Rex')" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fido\n", "German Shepherd\n", "Buddy\n", "Cocker\n", "Rex\n", "Royal Street Dog\n" ] } ], "source": [ "print(d.name)\n", "print(d.race)\n", "print(e.name)\n", "print(e.race)\n", "print(f.name)\n", "print(f.race)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "d.add_trick('Roll')\n", "d.add_trick('Pretending dead')" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Roll', 'Pretending Dead']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d.tricks" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "d.add_trick('Ask for food')" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Roll', 'Pretending Dead', 'Ask For Food']" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d.tricks" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "e.tricks" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Bark']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.add_trick('Bark')\n", "f.tricks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating a subclass using inheritance" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "class SuperDog(Dog):\n", " \n", " def __init__(self, name, race):\n", " Dog.__init__(self, name, race)\n", " self.food = False\n", " self.trained = False\n", " self.last_meal = time.time()\n", " \n", " def is_hungry(self):\n", " if time.time() - self.last_meal < 20:\n", " print('Not hungry')\n", " else:\n", " print(f'Yes, my last meal was {(time.time() - self.last_meal)/60:.2f} min. ago')\n", " \n", " def train(self):\n", " self.trained = True\n", " \n", " def feed(self):\n", " self.last_meal = time.time()\n", " print('The food was delicious. Thanks!')" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "f = SuperDog('Raghu','Labrador')" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Not hungry\n" ] } ], "source": [ "f.is_hungry()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Yes, my last meal was 0.44 min. ago\n" ] } ], "source": [ "f.is_hungry() #calling after some time..." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The food was delicious. Thanks!\n" ] } ], "source": [ "f.feed()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Not hungry\n" ] } ], "source": [ "f.is_hungry()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.tricks" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "f.add_trick('Give Five')" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Give Five']" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.tricks" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.trained" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "f.train()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.trained" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Multiple inheritance](https://www.techbeamers.com/python-multiple-inheritance/)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " In class B\n" ] } ], "source": [ "class A: \n", " def rk(self): \n", " print(\" In class A\") \n", " \n", "class B(A): \n", " def rk(self): \n", " print(\" In class B\") \n", " \n", "class C(A): \n", " def rk(self): \n", " print(\"In class C\") \n", " \n", "# classes ordering \n", "class D(B, C): \n", " pass\n", " \n", "r = D() \n", "r.rk() " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another example:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "class Animal:\n", " def __init__(self, weight=0, height=0):\n", " self.weight = weight\n", " self.height = height" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "class Carnivore(Animal):\n", " def __init__(self, weight=0, height=0):\n", " super().__init__(weight, height)\n", "\n", " def say(self):\n", " raise NotImplementedError" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2000" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "TRex = Carnivore(2000)\n", "TRex.weight" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "TRex.height" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "class Wolf(Carnivore):\n", " def __init__(self, weight, height):\n", " super().__init__(weight, height)\n", " \n", " def say(self):\n", " #print(\"Bark! Bark!\")\n", " return \"Bark! Bark!\"" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "8\n", "45\n", "Bark! Bark!\n" ] } ], "source": [ "Jack = Wolf(8, 45)\n", "print(Jack.weight)\n", "print(Jack.height)\n", "print(Jack.say())" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "class Pet(Animal):\n", " def __init__(self, tutor, weight=0, height=0):\n", " super().__init__(weight, height)\n", " self.tutor = tutor" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "John\n", "35\n", "0\n" ] } ], "source": [ "fish = Pet('John', 35)\n", "\n", "print(fish.tutor)\n", "print(fish.weight)\n", "print(fish.height)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "class Cat(Carnivore, Pet):\n", " def __init__(self, weight, height, tutor):\n", " super(Carnivore, self).__init__(self, weight, height)\n", " print(self.weight)\n", " print(self.height)\n", " Pet.__init__(self, tutor, self.weight, self.height)\n", " print(self.tutor)\n", " \n", " def say(self):\n", " print(\"Meaw!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[C3 & MRO](https://en.wikipedia.org/wiki/C3_linearization)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(__main__.Cat, __main__.Carnivore, __main__.Pet, __main__.Animal, object)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Cat.__mro__" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n", "25\n", "John\n" ] } ], "source": [ "Garfield = Cat(4, 25, 'John')" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Weight: 4\n", "Height: 25\n", "Tutor: John\n", "Meaw!\n" ] } ], "source": [ "print(f\"Weight: {Garfield.weight}\\nHeight: {Garfield.height}\\nTutor: {Garfield.tutor}\")\n", "Garfield.say()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modifying an existing class" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "class MyInteger(int):\n", " def __init__(self,number):\n", " super().__init__() \n", " \n", " def __add__(self, other):\n", " return self * other\n", " \n", " def __sub__(self, other):\n", " return self + other\n", " \n", " def square(self):\n", " return self * self" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "a = MyInteger(2)\n", "b = MyInteger(5)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['__abs__',\n", " '__add__',\n", " '__and__',\n", " '__bool__',\n", " '__ceil__',\n", " '__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__divmod__',\n", " '__doc__',\n", " '__eq__',\n", " '__float__',\n", " '__floor__',\n", " '__floordiv__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__getnewargs__',\n", " '__gt__',\n", " '__hash__',\n", " '__index__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__int__',\n", " '__invert__',\n", " '__le__',\n", " '__lshift__',\n", " '__lt__',\n", " '__mod__',\n", " '__module__',\n", " '__mul__',\n", " '__ne__',\n", " '__neg__',\n", " '__new__',\n", " '__or__',\n", " '__pos__',\n", " '__pow__',\n", " '__radd__',\n", " '__rand__',\n", " '__rdivmod__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__rfloordiv__',\n", " '__rlshift__',\n", " '__rmod__',\n", " '__rmul__',\n", " '__ror__',\n", " '__round__',\n", " '__rpow__',\n", " '__rrshift__',\n", " '__rshift__',\n", " '__rsub__',\n", " '__rtruediv__',\n", " '__rxor__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__sub__',\n", " '__subclasshook__',\n", " '__truediv__',\n", " '__trunc__',\n", " '__xor__',\n", " 'as_integer_ratio',\n", " 'bit_length',\n", " 'conjugate',\n", " 'denominator',\n", " 'from_bytes',\n", " 'imag',\n", " 'numerator',\n", " 'real',\n", " 'square',\n", " 'to_bytes']" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(a)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b #?????" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a - b" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.square()" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "25" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b.square()" ] }, { "cell_type": "code", "execution_count": 220, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on wrapper_descriptor:\n", "\n", "__mul__(self, value, /)\n", " Return self*value.\n", "\n" ] } ], "source": [ "help(MyInteger.__mul__)" ] }, { "cell_type": "code", "execution_count": 221, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on wrapper_descriptor:\n", "\n", "__str__(self, /)\n", " Return str(self).\n", "\n" ] } ], "source": [ "help(MyInteger.__str__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A more complete example:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "def strike(text):\n", " \"\"\" Renders string with strike-through characters through it.\n", "\n", " `strike('hello world')` -> '̶h̶e̶l̶l̶o̶ ̶w̶o̶r̶l̶d'\n", "\n", " Notes\n", " -----\n", " \\u0336 is a special strike-through unicode character; it\n", " is not unique to Python.\"\"\"\n", " return ''.join('\\u0336{}'.format(c) for c in text)\n", "\n", "class ShoppingList:\n", " def __init__(self, items):\n", " self._needed = set(items)\n", " self._purchased = set()\n", "\n", " def __repr__(self):\n", " \"\"\" Returns formatted shopping list as a string with\n", " purchased items being crossed out.\n", "\n", " Returns\n", " -------\n", " str\"\"\"\n", " if self._needed or self._purchased:\n", " remaining_items = [str(i) for i in self._needed]\n", " purchased_items = [strike(str(i)) for i in self._purchased]\n", " # You wont find the • character on your keyboard. I simply\n", " # googled \"unicode bullet point\" and copied/pasted it here.\n", " return \"• \" + \"\\n• \".join(remaining_items + purchased_items)\n", "\n", " def add_new_items(self, items):\n", " self._needed.update(items)\n", "\n", " def mark_purchased_items(self, items):\n", " self._purchased.update(set(items) & self._needed)\n", " self._needed.difference_update(self._purchased)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "• flour\n", "• salt\n", "• eggs\n", "• milk\n" ] } ], "source": [ "food = ShoppingList([\"milk\", \"flour\", \"salt\", \"eggs\"])\n", "print(food)" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "• eggs\n", "• milk\n", "• ̶f̶l̶o̶u̶r\n", "• ̶s̶a̶l̶t\n" ] } ], "source": [ "food.mark_purchased_items([\"flour\", \"salt\"])\n", "print(food)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "def __add__(self, other):\n", " \"\"\" Add the unpurchased and purchased items from another shopping\n", " list to the present one.\n", "\n", " Parameters\n", " ----------\n", " other : ShoppingList\n", " The shopping list whose items we will add to the present one.\n", " Returns\n", " -------\n", " ShoppingList\n", " The present shopping list, with items added to it.\"\"\"\n", " new_list = ShoppingList([])\n", " # populate new_list with items from `self` and `other`\n", " for l in [self, other]:\n", " new_list.add_new_items(l._needed)\n", "\n", " # add purchased items to list, then mark as purchased\n", " new_list.add_new_items(l._purchased)\n", " new_list.mark_purchased_items(l._purchased)\n", " return new_list\n", "\n", "# set `__add__` as a method of `ShoppingList`\n", "setattr(ShoppingList, \"__add__\", __add__)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "• t-shirts\n", "• pens\n", "• staples\n", "• socks\n", "• eggs\n", "• milk\n", "• ̶f̶l̶o̶u̶r\n", "• ̶s̶a̶l̶t\n", "• ̶p̶e̶n̶c̶i̶l̶s" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "food = ShoppingList([\"milk\", \"flour\", \"salt\", \"eggs\"])\n", "food.mark_purchased_items([\"flour\", \"salt\"])\n", "\n", "office_supplies = ShoppingList([\"staples\", \"pens\", \"pencils\"])\n", "office_supplies.mark_purchased_items([\"pencils\"])\n", "\n", "clothes = ShoppingList([\"t-shirts\", \"socks\"])\n", "\n", "# combine all three shopping lists\n", "food + office_supplies + clothes" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "class MyList:\n", " def __init__(self, *args):\n", " if len(args) == 1 and hasattr(args[0], '__iter__'):\n", " # handles `MyList([1, 2, 3])\n", " self._data = list(args[0])\n", " else:\n", " # handles `MyList(1, 2, 3)`\n", " self._data = list(args)\n", "\n", " def __getitem__(self, index):\n", " out = self._data[index]\n", " # slicing should return a `MyList` instance\n", " # otherwise, the individual element should be returned as-is\n", " return MyList(out) if isinstance(index, slice) else out\n", "\n", " def __setitem__(self, key, value):\n", " self._data[key] = value\n", "\n", " def __len__(self):\n", " return len(self._data)\n", "\n", " def __repr__(self):\n", " \"\"\" Use the character | as the delimiter for our list\"\"\"\n", " # `self._data.__repr__()` returns '[ ... ]',\n", " # thus we can slice to get the contents of the string\n", " # and exclude the square-brackets, and add our own\n", " # delimiters in their place\n", " return \"|\" + self._data.__repr__()[1:-1] + \"|\"\n", "\n", " def __contains__(self, item):\n", " return item in self._data\n", "\n", " def append(self, item):\n", " self._data.append(item)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "|'h', 'e', 'l', 'l', 'o'|" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# MyList can accept any iterable as its\n", "# first (and only) input argument\n", "x = MyList(\"hello\")\n", "x" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "|1, 2, 3, 4, 5|" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# MyList accepts an arbitrary number of arguments\n", "x = MyList(1, 2, 3, 4, 5)\n", "x" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n" ] } ], "source": [ "print(len(x))" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# getting an item\n", "x[0]" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "|3, 4|" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# slicing returns a MyList instance\n", "x[2:4]" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "|-1, 2, 3, 4, 5|" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# setting an item\n", "x[0] = -1\n", "x" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# checking membership\n", "10 in x" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "||" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MyList()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }