diff --git a/notebooks/Ch14-OOP.ipynb b/notebooks/Ch14-OOP.ipynb index e3d52da..55f7216 100644 --- a/notebooks/Ch14-OOP.ipynb +++ b/notebooks/Ch14-OOP.ipynb @@ -57,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -67,37 +67,18 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "<__main__.Point at 0x7ff0f8815820>" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "a" + "type(a)" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0 0\n" - ] - } - ], + "outputs": [], "source": [ "a.x = 0 # dynamically attach attriutes\n", "a.y = 0\n", @@ -106,7 +87,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -115,21 +96,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'Point' object has no attribute 'x'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/var/folders/4f/1pkkv7h960j42p0ppgk9n4ywjr6t_b/T/ipykernel_45410/1440403333.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: 'Point' object has no attribute 'x'" - ] - } - ], + "outputs": [], "source": [ "b.x" ] @@ -144,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -170,6 +139,15 @@ " Point.count -= 1" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(Point.count)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -187,7 +165,69 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = Point()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(a.x, a.y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b = Point(10, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(b.x, b.y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Point.count" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<__main__.Point object at 0x7f850fddcfa0>\n" + ] + } + ], + "source": [ + "print(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -197,7 +237,7 @@ " # what is the access specifier for attributes?\n", " print('p: x = {} and y = {}'.format(p.x, p.y))\n", " print(\"Total point objects = {}\".format(Point.count)) # access class variable outside class\n", - " #p.__del__() # call destructor explictly\n", + " p.__del__() # call destructor explictly\n", " p1 = Point(10, 100)\n", " print(\"p1: x = {} and y = {}\".format(p1.x, p1.y))\n", " print(\"Total point objects = {}\".format(Point.count))\n", @@ -208,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -216,26 +256,26 @@ "output_type": "stream", "text": [ "p: x = 0 and y = 0\n", - "Total point objects = 1\n", + "Total point objects = 3\n", "p1: x = 10 and y = 100\n", - "Total point objects = 2\n" + "Total point objects = 4\n" ] } ], "source": [ - "someFunction()\n" + "someFunction()" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Total point objects = 0\n" + "Total point objects = 2\n" ] } ], @@ -245,7 +285,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -253,9 +293,9 @@ "output_type": "stream", "text": [ "p: x = 0 and y = 0\n", - "Total point objects = 1\n", + "Total point objects = 3\n", "p1: x = 10 and y = 100\n", - "Total point objects = 2\n" + "Total point objects = 4\n" ] } ], @@ -272,14 +312,14 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "<__main__.Point object at 0x7f94e48e3730> <__main__.Point object at 0x7f94e48e3820>\n" + "<__main__.Point object at 0x7f850fe356c0> <__main__.Point object at 0x7f850fe097b0>\n" ] } ], @@ -310,7 +350,36 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "def dist_from_origin(self):\n", + " import math\n", + " dist = math.sqrt(self.x**2+self.y**2)\n", + " return dist" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100.4987562112089\n" + ] + } + ], + "source": [ + "print(dist_from_origin(p1))" + ] + }, + { + "cell_type": "code", + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ @@ -332,7 +401,7 @@ " return dist\n", " \n", " def __str__(self):\n", - " return \"({}, {})\".format(self.x, self.y)\n", + " return f\"({self.x}, {self.y})\"\n", " \n", " # destructor \n", " def __del__(self):\n", @@ -341,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 64, "metadata": {}, "outputs": [ { @@ -359,7 +428,36 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "p2 = Point(10, 200)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "200.24984394500785" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p2.dist_from_origin()" + ] + }, + { + "cell_type": "code", + "execution_count": 62, "metadata": {}, "outputs": [ { @@ -385,18 +483,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(3, 2)\n", - "(sasf, 10)\n" - ] - } - ], + "outputs": [], "source": [ "p2 = Point(3, 2)\n", "print(p2)\n", @@ -410,18 +499,19 @@ "metadata": {}, "source": [ "### better approach to change state/attribute is via methods\n", + "\n", "- move(xx, yy) method is added to class to set new x and y values for a point objects\n", "\n", "### Member access specifiers\n", + "\n", "- Python doesn't support `private`, `public`, `protected` specifiers provided by C++, Java, etc.\n", "- all the members are public by default\n", - "- however, it uses `__` double leading underscore notation to treat members as private\n", - " - this, however is not enforced" + "- however, you can use leading single `_` and double `__` underscores \"convention\" to treat members as private " ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -429,60 +519,67 @@ " \"\"\"\n", " Point class represents and manipulates x and y coordinates\n", " \"\"\"\n", - " count = 0\n", " \n", " def __init__(self, xx=0, yy=0):\n", " \"\"\"Create a new point with given x and y coords\"\"\"\n", - " self.x = xx\n", - " self.y = yy\n", - " Point.count += 1\n", + " \"\"\"_x and _y are protected method\"\"\"\n", + " \n", + " self._x = xx\n", + " self._y = yy\n", " \n", " def dist_from_origin(self):\n", " import math\n", - " dist = math.sqrt(self.x**2+self.y**2)\n", + " dist = math.sqrt(self._x**2+self._y**2)\n", " return dist\n", " \n", " def __str__(self): # string representation of the class; useful in printing objects\n", - " return \"({}, {})\".format(self.x, self.y)\n", + " return f\"({self._x}, {self._y})\"\n", + " \n", + " # define getter method to get x\n", + " def getX(self):\n", + " if not self._x:\n", + " return 0\n", + " return self._x\n", + " \n", + " # better syntax\n", + " @property\n", + " def x(self):\n", + " return self._x\n", " \n", " # use setters to set attributes\n", " def setX(self, xx):\n", " if isinstance(xx, int) or isinstance(xx, float):\n", - " self.x = int(xx)\n", + " self._x = int(xx)\n", " elif isinstance(xx, str):\n", " if xx.isnumeric():\n", - " self.x = int(xx)\n", + " self._x = int(xx)\n", " \n", - " def setY(self, yy):\n", + " # better syntax for setter\n", + " @x.setter\n", + " def x(self, xx: int):\n", + " self.setX(xx)\n", + " \n", + " @property\n", + " def y(self):\n", + " return self._y\n", + " \n", + " @y.setter\n", + " def y(self, yy: int):\n", " if isinstance(yy, int) or isinstance(yy, float):\n", - " self.y = int(yy)\n", + " self._y = int(yy)\n", " elif isinstance(yy, str):\n", " if yy.isnumeric():\n", - " self.y = int(yy)\n", - " \n", - " # use getters to get attributes\n", - " def getX(self):\n", - " if not self.x:\n", - " return 0\n", - " return self.x\n", - " \n", - " def getY(self):\n", - " return self.y\n", + " self._y = int(yy)\n", + "\n", " \n", " def move(self, xx, yy):\n", - " self.setX(xx)\n", - " self.setY(yy)\n", - " #self.x = xx\n", - " #self.y = yy\n", - " \n", - " # destructor\n", - " def __del__(self):\n", - " Point.count -= 1" + " self.x = xx\n", + " self.y = yy" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -503,7 +600,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -512,7 +609,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -531,23 +628,142 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## sameness - alias or deep copy" + "## Operator Overloading\n", + "\n", + "- https://docs.python.org/3/reference/datamodel.html\n", + "- Python lets you overload special operators (e.g., +, -, /, //, %, etc.) for your class\n", + "- Goal is to make your class as easy and seamless to use as possible\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "class Point:\n", + " \"\"\"\n", + " Point class represents and manipulates x and y coordinates\n", + " \"\"\"\n", + " \n", + " def __init__(self, xx=0, yy=0):\n", + " \"\"\"Create a new point with given x and y coords\"\"\"\n", + " \"\"\"_x and _y are protected method\"\"\"\n", + " \n", + " self._x = xx\n", + " self._y = yy\n", + " \n", + " def dist_from_origin(self):\n", + " import math\n", + " dist = math.sqrt(self._x**2+self._y**2)\n", + " return dist\n", + " \n", + " def __str__(self): # string representation of the class; useful in printing objects\n", + " return f\"({self._x}, {self._y})\"\n", + " \n", + " @property\n", + " def x(self):\n", + " if not self._x:\n", + " return 0\n", + " return self._x\n", + " \n", + " @x.setter\n", + " def x(self, xx: int):\n", + " if isinstance(xx, int) or isinstance(xx, float):\n", + " self._x = int(xx)\n", + " elif isinstance(xx, str):\n", + " if xx.isnumeric():\n", + " self._x = int(xx)\n", + " \n", + " @property\n", + " def y(self):\n", + " return self._y\n", + " \n", + " @y.setter\n", + " def y(self, yy: int):\n", + " if isinstance(yy, int) or isinstance(yy, float):\n", + " self._y = int(yy)\n", + " elif isinstance(yy, str):\n", + " if yy.isnumeric():\n", + " self._y = int(yy)\n", + "\n", + " \n", + " def move(self, xx, yy):\n", + " self.x = xx\n", + " \n", + " def __add__(self, other: \"Point\"):\n", + " \"\"\"Overload + operator\"\"\"\n", + " new_x = self.x + other.x\n", + " new_y = self.y + other.y\n", + " return Point(new_x, new_y)\n", + " \n", + " \n", + " def __eq__(self, other: \"Point\"):\n", + " return self.x == other.x and self.y == other.y" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "True\n", - "False\n" + "(5, 9)\n" ] } ], + "source": [ + "p1 = Point(2, 4)\n", + "p2 = Point(3, 5)\n", + "p3 = p1+p2\n", + "print(p3)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "assert p1 != p2" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[25], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m p1 \u001b[38;5;241m==\u001b[39m p2\n", + "\u001b[0;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "assert p1 == p2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## sameness - alias or deep copy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import copy\n", "p2 = Point(3, 4)\n", @@ -566,11 +782,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def print_point(pt):\n", + "def print_point(pt: Point):\n", " pt.x = 100\n", " pt.y = 100\n", " print(pt)" @@ -578,21 +794,20 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(100, 100)\n", - "(100, 100)\n" - ] - } - ], + "outputs": [], "source": [ "p = Point(10, 10)\n", - "print_point(p)\n", + "print_point(p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "print(p)" ] }, @@ -615,7 +830,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -628,17 +843,9 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(5, 5)\n" - ] - } - ], + "outputs": [], "source": [ "p = Point(4, 6)\n", "q = Point(6, 4)\n", @@ -666,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -685,18 +892,9 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "box: ((0, 0), 100, 200)\n", - "bomb: ((100, 80), 5, 10)\n" - ] - } - ], + "outputs": [], "source": [ "box = Rectangle(Point(0, 0), 100, 200)\n", "bomb = Rectangle(Point(100, 80), 5, 10) # In my video game\n", @@ -832,7 +1030,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -853,7 +1051,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -862,60 +1060,27 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('instance method called', <__main__.MyClass at 0x7f94e4901820>)" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "c.method()" ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('class method called', __main__.MyClass)" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "MyClass.classmethod()" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'static method called'" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "MyClass.staticmethod()" ] @@ -981,13 +1146,6 @@ "# because of exception due to invalid grades, class_grades_invalid object is never created\n", "print(class_grades_invalid.grades)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -1006,7 +1164,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.8" } }, "nbformat": 4,