diff --git a/installation/environment.yml b/installation/environment.yml index 16b33223..fa8eea0c 100644 --- a/installation/environment.yml +++ b/installation/environment.yml @@ -4,7 +4,7 @@ channels: - nodefaults dependencies: # required - - python=3.12 + - python - pip - numpy @@ -36,14 +36,12 @@ dependencies: - uxarray - pyyaml - rtree - - pyvista - - vtk - imageio - requests - pytest - statsmodels - dataretrieval - - flopy=3.9 + - flopy - gis-utils - sfrmaker - contextily diff --git a/notebooks/part0_python_intro/02_Namespace_objects_modules_packages.ipynb b/notebooks/part0_python_intro/02_Namespace_objects_modules_packages.ipynb index 0ae159f7..fcd991aa 100644 --- a/notebooks/part0_python_intro/02_Namespace_objects_modules_packages.ipynb +++ b/notebooks/part0_python_intro/02_Namespace_objects_modules_packages.ipynb @@ -469,7 +469,12 @@ "cell_type": "code", "execution_count": null, "id": "cfc5092f", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2026-07-13T20:33:01.937415100Z", + "start_time": "2026-07-13T20:33:01.902023300Z" + } + }, "outputs": [], "source": [ "class Rectangle(object):\n", @@ -487,7 +492,12 @@ "cell_type": "code", "execution_count": null, "id": "a9e31831", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2026-07-13T20:33:01.993260200Z", + "start_time": "2026-07-13T20:33:01.940307700Z" + } + }, "outputs": [], "source": [ "print(Rectangle)" @@ -497,7 +507,12 @@ "cell_type": "code", "execution_count": null, "id": "99393550", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2026-07-13T20:33:02.047728Z", + "start_time": "2026-07-13T20:33:02.008492Z" + } + }, "outputs": [], "source": [ "r1 = Rectangle(2, 3, 'f')\n", @@ -510,7 +525,12 @@ "cell_type": "code", "execution_count": null, "id": "a0ba121a", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2026-07-13T20:34:14.783207400Z", + "start_time": "2026-07-13T20:34:14.763440600Z" + } + }, "outputs": [], "source": [ "# attrs" @@ -519,22 +539,18 @@ { "cell_type": "code", "execution_count": null, - "id": "efbd4d86", + "id": "8f472293-767f-40bd-9996-bce5ec3fe37c", "metadata": {}, "outputs": [], - "source": [ - "# in a list" - ] + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "b0a41016", + "id": "19c3ed95-cdbc-48a5-85bd-dc18f7a1d9b7", "metadata": {}, "outputs": [], - "source": [ - "# attrs in list" - ] + "source": [] }, { "cell_type": "code", @@ -586,9 +602,7 @@ "id": "7a70da84", "metadata": {}, "outputs": [], - "source": [ - "big_rectangle.length" - ] + "source": [] }, { "cell_type": "markdown", @@ -604,54 +618,6 @@ "id": "170deebd", "metadata": {}, "outputs": [], - "source": [ - "print(big_rectangle.width)\n", - "print(big_rectangle.length)\n", - "print(big_rectangle.ID)" - ] - }, - { - "cell_type": "markdown", - "id": "fac41549", - "metadata": {}, - "source": [ - "We can use this set of attributes as a kind of database.\n", - "\n", - "### Questions\n", - "\n", - "How could we make a group of rectangles of varying lengths and widths?\n", - " - We could make each attribute a list.\n", - " - We could make a list of instances with a member for each rectangle." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f478a217", - "metadata": {}, - "outputs": [], - "source": [ - "myrectangles = Rectangle([25, 78], [44, 42], ['r1', 'r2'])\n", - "print(myrectangles)\n", - "myrectangles.width[0]\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a8f0e68", - "metadata": {}, - "outputs": [], - "source": [ - "# live code a better way to do this\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ceee7ba7", - "metadata": {}, - "outputs": [], "source": [] }, { @@ -660,15 +626,7 @@ "metadata": {}, "source": [ "### Test your skills\n", - "Could we do this is a dictionary rather than a list?" - ] - }, - { - "cell_type": "markdown", - "id": "ec012e3b", - "metadata": {}, - "source": [ - "keys ---> 'R1' 'R2'" + "Can we create a list or dictionary of Rectangle objects?" ] }, { @@ -677,7 +635,11 @@ "id": "3248b98f", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "widths = [21, 10, 14]\n", + "heights = [5, 3, 10]\n", + "ids = [\"r1\", \"r2\", \"r3\"]" + ] }, { "cell_type": "code", @@ -703,14 +665,6 @@ "outputs": [], "source": [] }, - { - "cell_type": "markdown", - "id": "653dd7ad", - "metadata": {}, - "source": [ - "There are advantages to both approaches. It would also be possible to define each attribute as a list or dictionary and make a single class. This is a bit more cumbersome, though, and part of the flexibility of dynamic lists and dictionaries is the ability to define multiple objects within them on the fly." - ] - }, { "cell_type": "markdown", "id": "58e35f97", @@ -721,242 +675,46 @@ "Now say we want to operate on these data, like to calculate the area of each rectangle." ] }, - { - "cell_type": "markdown", - "id": "455b3100", - "metadata": {}, - "source": [ - "### Test your skills\n", - "In the blank code block below, calculate the areas for each rectangle using a loop." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77837620", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5146ce31", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5a274a8", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "eeb6682e", - "metadata": {}, - "source": [ - "Is there a more efficient way do this if we know, for example, that area will be of interest?\n", - "\n", - "We can create a method at definition of the class that uses the attributes of `length` and `width` to derive area if called and store it as an additional attribute." - ] - }, { "cell_type": "code", "execution_count": null, - "id": "ac48dc3f", - "metadata": {}, + "id": "f17dae1f-8061-4e03-a90f-6e8e967419fe", + "metadata": { + "ExecuteTime": { + "end_time": "2026-07-13T20:33:01.937415100Z", + "start_time": "2026-07-13T20:33:01.902023300Z" + } + }, "outputs": [], "source": [ "class Rectangle(object):\n", + " \"\"\"\n", + " this is a doc string\n", + " \"\"\"\n", + " #this is just a comment\n", " def __init__(self, x, y, ID):\n", " self.length = x\n", " self.width = y\n", " self.ID = ID\n", - " \n", - " def calc_area(self):\n", - " # we only pass self because there are no additional attributes of concern\n", - " self.area = self.length * self.width" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8cd57fa", - "metadata": {}, - "outputs": [], - "source": [ - "rr = Rectangle(3,4,'this')\n", - "vars(rr)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f49aeb69", - "metadata": {}, - "outputs": [], - "source": [ - "rr.calc_area()\n", - "rr.__dict__" - ] - }, - { - "cell_type": "markdown", - "id": "5d9c428c", - "metadata": {}, - "source": [ - "Let's make our list of `rectangle` objects again, but use a method to calculate the areas." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "49ca5af4", - "metadata": {}, - "outputs": [], - "source": [ - "all_rectangles = list()\n", - "all_rectangles.append(Rectangle(35,25,'rectangle one'))\n", - "all_rectangles.append(Rectangle(150, 1000, 'big dog'))\n", - "vars(all_rectangles[0])\n", - "all_rectangles[0].__dict__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c5e8a", - "metadata": {}, - "outputs": [], - "source": [ - "for csqr in all_rectangles:\n", - " csqr.calc_area()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "04c34297", - "metadata": {}, - "outputs": [], - "source": [ - "# let's loop over again to show the area\n", - "for csqr in all_rectangles:\n", - " print(f\"{csqr.ID} area={csqr.area}\")\n", - "vars(all_rectangles[0])" - ] - }, - { - "cell_type": "markdown", - "id": "2002b019", - "metadata": {}, - "source": [ - "We could even incorporate calculations into the `__init__` constructor so the area is calculated on instantiation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f4b5ee57", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.area = x * y\n", - " self.ID = ID" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "befb2a0a", - "metadata": {}, - "outputs": [], - "source": [ - "rr = Rectangle(4, 5,'bummer')\n", - "rr.__dict__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c5228679", - "metadata": {}, - "outputs": [], - "source": [ - "# what happens when we change the length?" - ] - }, - { - "cell_type": "markdown", - "id": "8a9e3fdf", - "metadata": {}, - "source": [ - "Uh oh! Length changes but the overall area of the rectangle doesn't\n", "\n", - "### Properties to the rescue!!!\n", - "\n", - "Properties are a special method that behaves similar to an attribute. These methods allow for on the fly (\"dynamic\") calculations and variable construction among other things. \n", - "\n", - "Properties are defined with a special decorator (`@property`). Decorators are an advanced topic and won't be covered in this course. More information about decorators, how they are used, and how they work can be found [here](https://betterprogramming.pub/decorators-in-python-72a1d578eac4).\n" + " # live code methods" ] }, { "cell_type": "code", "execution_count": null, - "id": "197762a7", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aeb8ff44", + "id": "5146ce31", "metadata": {}, "outputs": [], - "source": [ - "rect = Rectangle(5, 9, \"awesome rectangle\")\n", - "print(rect.__dict__)\n", - "print(rect.area)" - ] - }, - { - "cell_type": "markdown", - "id": "d932bd8d", - "metadata": {}, - "source": [ - "what happens if the length of the rectangle grows?" - ] + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "3de3b47d", + "id": "a5a274a8", "metadata": {}, "outputs": [], - "source": [ - "for new_length in range(5, 100, 11):\n", - " rect.length = new_length\n", - " print(f\"ID={rect.ID}, length={rect.length}, width={rect.width}, area={rect.area}\")" - ] + "source": [] }, { "cell_type": "markdown", @@ -965,13 +723,13 @@ "source": [ "### Test your skills!!!\n", "\n", - "build an even better `Rectangle` class that includes an `area` and `perimiter_length` property method.\n" + "build an even better `Rectangle` class that includes an `area` and `perimeter` method.\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "e33ce1d5", + "id": "5edeace1", "metadata": {}, "outputs": [], "source": [] @@ -979,7 +737,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4314d746", + "id": "165cd5dc-7341-44c8-a10c-c0a8bdd0a480", "metadata": {}, "outputs": [], "source": [] @@ -987,93 +745,27 @@ { "cell_type": "code", "execution_count": null, - "id": "591426db", + "id": "349fe0f7-b93f-4d38-a34c-2bad9228bafd", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", - "id": "0795056b", + "id": "4456833a-f6b9-4112-8ea7-97e794ba8172", "metadata": {}, "source": [ - "## Operator and Special Method Overloading*\n", - "\n", - "One special thing we can do is overload operators. This means we can customize the behavior that an object (as an instance of a class) will exhibit when called. The `__init__` constructor was an example of this which we have done something similar already. When an instance is made from a class, whatever is defined in `__init__` is performed as part of creating the instance which is effectively _overloading_ the special method `__init__`.\n", + "### A note on property methods\n", "\n", - "Another really common example is `__str__`. This function provides a string for Python to display when `print` of `str` is called on an object.\n", - "\n", - "Looking at the example of `rectangle` objects we used above. \n", - "\n", - "\\*N.B. --> The concept of `overloading` is different in Python than in FORTRAN. Also, there appears to be a lack of precision about the use of the terms `overload` and `override`. I'm using the terminology from the _O' Reilly_ book here, but note that you might find other people using `override` instead. The concept in this case is the same." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73084666", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __str__(self):\n", - " return f\"I'm a Rectangle and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfa0dedb", - "metadata": {}, - "outputs": [], - "source": [ - "rect = Rectangle(3, 4, 'super_awesome_rectangle')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7be7aff8", - "metadata": {}, - "outputs": [], - "source": [ - "rect" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1d2d2b1b", - "metadata": {}, - "outputs": [], - "source": [ - "print(rect)" - ] - }, - { - "cell_type": "markdown", - "id": "50207d92", - "metadata": {}, - "source": [ - "Notice that this prints out a default string telling us the `rectangle` is an object in this code (e.g. under `__main__`). But, we can _overload_ and provide a more useful return string using `__repr__`. This serves the same purpose as `__str__` but also returns the string in other circumstances.\n", + "Properties are a special method that behaves similar to an attribute. These methods allow for on the fly (\"dynamic\") calculations and variable construction among other things. \n", "\n", - "What's the difference between `__repr__` and `__str__`? The Diet Mountain Dew crew has discussed this [here](http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python)" + "Properties are defined with a special decorator (`@property`). Decorators are an advanced topic and won't be covered in this course. More information about decorators, how they are used, and how they work can be found [here](https://betterprogramming.pub/decorators-in-python-72a1d578eac4).\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "0c730327", + "id": "7e7c2151-e284-4524-bff8-096b9ca03709", "metadata": {}, "outputs": [], "source": [ @@ -1086,118 +778,16 @@ " @property\n", " def area(self):\n", " return self.length * self.width\n", - " \n", - " def __repr__(self):\n", - " return f\"I'm a Rectangle and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "79d44ebb", - "metadata": {}, - "outputs": [], - "source": [ - "rect = Rectangle(3, 4, 'super_awesome_rectangle')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d88d2037", - "metadata": {}, - "outputs": [], - "source": [ - "rect" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6786fc9c", - "metadata": {}, - "outputs": [], - "source": [ - "print(rect)" - ] - }, - { - "cell_type": "markdown", - "id": "0ffee05f", - "metadata": {}, - "source": [ - "We can even overload other operators like `__add__` which will control what happens when this object is added to another.\n", - "\n", - "A complete list of which special methods and operators can be overloaded is found [here](https://docs.python.org/3.9/reference/datamodel.html#special-method-names). \n", "\n", - "A short list of standard operators for mathematical functions can be found [here](https://docs.python.org/3/library/operator.html)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c9876320", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __add__(self, other):\n", - " print(\"what should we add today???\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a4d7337d", - "metadata": {}, - "outputs": [], - "source": [ - "r1 = Rectangle(1, 1, \"Small rect\")\n", - "r2 = Rectangle(3, 4, \"bigger one\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e094e21a", - "metadata": {}, - "outputs": [], - "source": [ - "r1 + r2" + " def perimeter(self):\n", + " return 2 * (self.length + self.width)\n" ] }, - { - "cell_type": "markdown", - "id": "bce3ff8d", - "metadata": {}, - "source": [ - "### Test your skills -- overload `__add__` so that adding two rectangles adds their area\n", - "\n", - "Start with the class we just made. HINT: you will need to represent the other object with `other`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bea8d230", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, - "id": "d260a39b", + "id": "098f05e2-1848-4163-95d6-c9fa62bcd313", "metadata": {}, "outputs": [], "source": [] @@ -1205,42 +795,31 @@ { "cell_type": "code", "execution_count": null, - "id": "5edeace1", + "id": "993bf3e1-682c-4cf2-bd2b-e4ba9087ab28", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", - "id": "6146c874", + "id": "c7584055-9fcf-4dd4-8553-a950ef7fa00e", "metadata": {}, "source": [ - "Operator overloading is very powerful, but with great power comes great responsibility. Use with caution." - ] - }, - { - "cell_type": "markdown", - "id": "e0869202", - "metadata": {}, - "source": [ - "## Object-oriented programming (OOP)\n", + "### Just for funsies, let's extend the class to check if we made a golden rectangle\n", "\n", - "In the O'Reilly book _Learning Python, 5th Edition_ is a great discussion about Object-Oriented Programming. The author makes this distinction that much of what we are doing with Python is _object-based_ but to truly be object-oriented, we need to also use something called inheritance." - ] - }, - { - "cell_type": "markdown", - "id": "842f678c", - "metadata": {}, - "source": [ - "## Inheritance\n", - "Let's revisit our class for rectangles without the overloading of `__add__`" + "In mathematics, the golden ratio between two values occurs if their ratio is the same as the ratio of their sum over the larger of the two quantities. We can represent that as:\n", + "\n", + "$\\frac{a + b}{a} = \\frac{a}{b}$\n", + "\n", + "For a rectangle:\n", + " - a = shorter side\n", + " - b = longer side - shorter side" ] }, { "cell_type": "code", "execution_count": null, - "id": "d5f3f918", + "id": "ae159652-4d4c-4502-b607-c6fdf088f3ec", "metadata": {}, "outputs": [], "source": [ @@ -1249,70 +828,20 @@ " self.length = x\n", " self.width = y\n", " self.ID = ID\n", - " self._type = \"Rectangle\"\n", " \n", " @property\n", " def area(self):\n", " return self.length * self.width\n", - " \n", - " def __repr__(self):\n", - " return f\"I'm a {self._type} and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"\n", - " " - ] - }, - { - "cell_type": "markdown", - "id": "9b3402f0", - "metadata": {}, - "source": [ - "We can _inherit_ these characteristics (the methods and properties) in a new kind of class that has a custom bit of functionality. Say we would like to create a second class that is specifically for representing squares.\n", "\n", - "We can redefine a new class _inheriting_ the rectangle attributes and methods. We can even add the new functionality on top of it.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7da06c5", - "metadata": {}, - "outputs": [], - "source": [ - "# Rectangle is the parent class\n", - "class Square(Rectangle):\n", - " \"\"\"\n", - " Doc strings for Square\n", - " \"\"\"\n", - " def __init__(self, x, ID):\n", - " # initialize the parent class using the super().__init__ call\n", - " super().__init__(x, x, ID)\n", - " self._type = \"Square\"\n", - " \n", " @property\n", - " def perimiter(self):\n", - " return 2 * (self.length + self.width)\n", - " \n", - " def __repr__(self):\n", - " # get the __repr__ from Rectangle using super()\n", - " s = super().__repr__()\n", - " s += f\", perimiter={self.perimiter}\"\n", - " return s" + " def perimeter(self):\n", + " return 2 * (self.length + self.width)\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "befe6094", - "metadata": {}, - "outputs": [], - "source": [ - "# live code example for rectangle and square" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "83bc5cf7", + "id": "b79f8cfa-5ce1-448f-a3c0-b3332a4ca221", "metadata": {}, "outputs": [], "source": [] @@ -1320,7 +849,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6b4666b6", + "id": "22b6aff2-0e5b-40cc-ab0a-26fd2b850434", "metadata": {}, "outputs": [], "source": [] @@ -1338,23 +867,23 @@ "\n", "Packages are collections of modules and are often \"installed\" to be accessible to Python from anywhere. More on that at the end of the lesson.\n", "\n", - "Let's import a module and find a function within it." + "Let's import a module like `random` and find a function within it." ] }, { "cell_type": "code", "execution_count": null, - "id": "2f526fd3", + "id": "6e879bae-6f77-414a-ac34-76a11c7c436e", "metadata": {}, "outputs": [], "source": [ - "# live code example\n" + "# live code example" ] }, { "cell_type": "code", "execution_count": null, - "id": "529c40bb", + "id": "4cda9502-824a-46a1-86d5-cafcb3b42242", "metadata": {}, "outputs": [], "source": [] @@ -1362,7 +891,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cb76abf2", + "id": "988184f7-1e93-4ff9-8b28-64537dcfb234", "metadata": {}, "outputs": [], "source": [] @@ -1585,36 +1114,17 @@ "id": "58f54355", "metadata": {}, "source": [ - "## Exercise: putting it all together\n", + "## Exercise (if there's time): putting it all together\n", "\n", "In this exercise we'll create our first module and import it into this notebook.\n", "\n", - "Open the python file \"circle_module.py\" in an IDE or text editor and create a class called `Circle`. Inputs to circle should be a radius and ID. Include in the `Circle` class a way to the calculate area and the circumference. After building the class, try importing into this notebook and using it.\n", - "\n", - "**Bonus exercise:** Find a way to make the `Circle` objects divisible and compare the difference in area between a 12\" and 16\" pizza. \n", - "**Bonus exercise 2:** Add additonal functionality to compare the price per square inch of the two pizzas. Ian's pizza in Madison, WI lists sizes and prices on their website which could be used as a reference." + "Open the python file \"circle_module.py\" in an IDE or text editor and create a class called `Circle`. Inputs to circle should be a radius and ID. Include in the `Circle` class a way to the calculate area and the circumference. After building the class, try importing into this notebook and using it.\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "31ebba7f", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ffa39b59", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "942c7596", + "id": "211ace03", "metadata": {}, "outputs": [], "source": [] @@ -1622,7 +1132,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1f7b1dc6", + "id": "7aa1f4a5-f94f-4762-8c65-8b6dc8e4c840", "metadata": {}, "outputs": [], "source": [] @@ -1630,7 +1140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "211ace03", + "id": "91317147-7395-4d7f-a3d1-e06ce8488848", "metadata": {}, "outputs": [], "source": [] @@ -1641,6 +1151,18 @@ "display_name": "Python 3 (ipykernel)", "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.14.6" } }, "nbformat": 4, diff --git a/notebooks/part0_python_intro/solutions/02_Namespace_objects_modules_packages__solution.ipynb b/notebooks/part0_python_intro/solutions/02_Namespace_objects_modules_packages__solution.ipynb index 09a27885..31da504d 100644 --- a/notebooks/part0_python_intro/solutions/02_Namespace_objects_modules_packages__solution.ipynb +++ b/notebooks/part0_python_intro/solutions/02_Namespace_objects_modules_packages__solution.ipynb @@ -70,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "54de4fff-549e-4629-8530-dbde2280d27b", "metadata": {}, "outputs": [], @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "90945336", "metadata": {}, "outputs": [ @@ -111,7 +111,7 @@ "5" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "1235392a", "metadata": {}, "outputs": [], @@ -142,7 +142,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "d40930d3", "metadata": {}, "outputs": [ @@ -152,7 +152,7 @@ "" ] }, - "execution_count": 4, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -163,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "6b77b909", "metadata": {}, "outputs": [ @@ -181,7 +181,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "883515a7", "metadata": {}, "outputs": [ @@ -191,7 +191,7 @@ "" ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -203,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "289e645b", "metadata": {}, "outputs": [ @@ -231,7 +231,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "fe256808", "metadata": {}, "outputs": [ @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "4968e004", "metadata": {}, "outputs": [ @@ -301,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "ed6fe608", "metadata": {}, "outputs": [ @@ -342,7 +342,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "116318ed", "metadata": {}, "outputs": [ @@ -380,7 +380,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "id": "eb864332", "metadata": {}, "outputs": [ @@ -425,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "d7fb1a05", "metadata": {}, "outputs": [ @@ -485,7 +485,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "4804faa0", "metadata": {}, "outputs": [], @@ -497,7 +497,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "bf5e893c", "metadata": {}, "outputs": [ @@ -507,7 +507,7 @@ "" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -518,7 +518,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "52e2160c", "metadata": {}, "outputs": [ @@ -536,7 +536,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "6a79bd58", "metadata": {}, "outputs": [ @@ -562,7 +562,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "0695b1e5", "metadata": {}, "outputs": [], @@ -578,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "11060bf3", "metadata": {}, "outputs": [ @@ -588,7 +588,7 @@ "__main__.Person" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -599,7 +599,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "f0a43ea0", "metadata": {}, "outputs": [ @@ -609,7 +609,7 @@ "'Fredrick'" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -621,17 +621,17 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "id": "26d2d689", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "<__main__.Person at 0x1106c4b50>" + "<__main__.Person at 0x20f3be6cc20>" ] }, - "execution_count": 21, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -642,7 +642,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "id": "4f2cf35a", "metadata": {}, "outputs": [ @@ -669,7 +669,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "dda9b64e", "metadata": {}, "outputs": [], @@ -687,7 +687,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "id": "fb6cf5b1", "metadata": {}, "outputs": [ @@ -705,7 +705,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "id": "abbbfdf3", "metadata": {}, "outputs": [ @@ -727,7 +727,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "id": "56464299", "metadata": {}, "outputs": [ @@ -737,7 +737,7 @@ "5" ] }, - "execution_count": 26, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -748,7 +748,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "id": "581f57ef", "metadata": {}, "outputs": [], @@ -758,7 +758,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "id": "54dabfbf", "metadata": {}, "outputs": [ @@ -768,7 +768,7 @@ "2" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -779,7 +779,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "id": "fc69e2b8", "metadata": {}, "outputs": [ @@ -821,7 +821,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "id": "3fae4530", "metadata": {}, "outputs": [ @@ -831,7 +831,7 @@ "{'length': 25, 'width': 35, 'ID': 'rectangle one'}" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -844,7 +844,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "id": "46662487", "metadata": {}, "outputs": [ @@ -854,7 +854,7 @@ "25" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -873,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "id": "673e0f63", "metadata": {}, "outputs": [ @@ -893,102 +893,13 @@ "print(big_rectangle.ID)" ] }, - { - "cell_type": "markdown", - "id": "bb716015", - "metadata": {}, - "source": [ - "We can use this set of attributes as a kind of database.\n", - "\n", - "### Questions\n", - "\n", - "How could we make a group of rectangles of varying lengths and widths?\n", - " - We could make each attribute a list.\n", - " - We could make a list of instances with a member for each rectangle." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "4a47da72", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "<__main__.Rectangle object at 0x111a111d0>\n" - ] - }, - { - "data": { - "text/plain": [ - "44" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "myrectangles = Rectangle([25, 78], [44, 42], ['r1', 'r2'])\n", - "print(myrectangles)\n", - "myrectangles.width[0]\n" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "db1bc601", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[<__main__.Rectangle at 0x111a1b050>, <__main__.Rectangle at 0x111a1afd0>]" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "myrectangles_better = list()\n", - "myrectangles_better.append(Rectangle(25, 44, 'r1'))\n", - "myrectangles_better.append(Rectangle(78, 42, 'r2'))\n", - "myrectangles_better" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "a75ec4c9", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "78" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "myrectangles_better[1].length" - ] - }, { "cell_type": "markdown", "id": "bc26bfe9", "metadata": {}, "source": [ "### Test your skills\n", - "Could we do this is a dictionary rather than a list?" + "Can we create a list or dictionary of Rectangle objects?" ] }, { @@ -1001,65 +912,90 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 34, "id": "2df1831a", "metadata": {}, "outputs": [], "source": [ - "d = dict()\n", - "d[\"R1\"] = Rectangle(3, 4, 'rect1')\n", - "d[\"R2\"] = Rectangle(5, 99, \"rect2\")" + "widths = [21, 10, 14]\n", + "heights = [5, 3, 10]\n", + "ids = [\"r1\", \"r2\", \"r3\"]" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 35, "id": "b4999719", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "3" + "[<__main__.Rectangle at 0x20f3be34fc0>,\n", + " <__main__.Rectangle at 0x20f3be350f0>,\n", + " <__main__.Rectangle at 0x20f3bdf5b50>]" ] }, - "execution_count": 37, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "d[\"R1\"].length" + "# list\n", + "rects = []\n", + "for lbl, w, h in zip(ids, widths, heights):\n", + " rects.append(Rectangle(w, h, lbl))\n", + "rects" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 36, "id": "4a7f7976", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "99" + "{'r1': <__main__.Rectangle at 0x20f3be4caf0>,\n", + " 'r2': <__main__.Rectangle at 0x20f3be4d150>,\n", + " 'r3': <__main__.Rectangle at 0x20f3bc15750>}" ] }, - "execution_count": 38, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "d[\"R2\"].width" + "# dict\n", + "rdict = {}\n", + "for lbl, w, h in zip(ids, widths, heights):\n", + " rdict[lbl] = Rectangle(w, h, lbl)\n", + "\n", + "rdict" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "id": "b9e3364d", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "21\n" + ] + } + ], + "source": [ + "print(rdict[\"r2\"].width)\n", + "print(rects[0].length)" + ] }, { "cell_type": "markdown", @@ -1080,43 +1016,49 @@ ] }, { - "cell_type": "markdown", - "id": "8b6e75f7", + "cell_type": "code", + "execution_count": 38, + "id": "511f027a", "metadata": {}, + "outputs": [], "source": [ - "### Test your skills\n", - "In the blank code block below, calculate the areas for each rectangle using a loop." + "class Rectangle(object):\n", + " \"\"\"\n", + " this is a doc string\n", + " \"\"\"\n", + " #this is just a comment\n", + " def __init__(self, x, y, ID):\n", + " self.length = x\n", + " self.width = y\n", + " self.ID = ID\n", + "\n", + " # live code methods\n", + " def calc_area(self):\n", + " return self.length * self.width" ] }, { "cell_type": "code", "execution_count": 39, - "id": "511f027a", + "id": "1923f4e0", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "12\n", - "495\n" - ] + "data": { + "text/plain": [ + "24.8" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for k, val in d.items():\n", - " area = val.length * val.width\n", - " print(area)" + "r8 = Rectangle(4, 6.2, \"r8\")\n", + "r8.calc_area()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "1923f4e0", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -1130,9 +1072,9 @@ "id": "d2cdfcb4", "metadata": {}, "source": [ - "Is there a more efficient way do this if we know, for example, that area will be of interest?\n", + "### Test your skills!!!\n", "\n", - "We can create a method at definition of the class that uses the attributes of `length` and `width` to derive area if called and store it as an additional attribute." + "build an even better `Rectangle` class that includes an `area` and `perimeter` method." ] }, { @@ -1143,14 +1085,22 @@ "outputs": [], "source": [ "class Rectangle(object):\n", + " \"\"\"\n", + " this is a doc string\n", + " \"\"\"\n", + " #this is just a comment\n", " def __init__(self, x, y, ID):\n", " self.length = x\n", " self.width = y\n", " self.ID = ID\n", - " \n", + "\n", + " # live code methods\n", " def calc_area(self):\n", - " # we only pass self because there are no additional attributes of concern\n", - " self.area = self.length * self.width" + " return self.length * self.width\n", + "\n", + " def calc_perimeter(self):\n", + " return 2 * (self.length + self.width)\n", + " " ] }, { @@ -1158,21 +1108,9 @@ "execution_count": 41, "id": "19f66a7e", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'length': 3, 'width': 4, 'ID': 'this'}" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "rr = Rectangle(3,4,'this')\n", - "vars(rr)" + "r34 = Rectangle(3, 4.345, \"another_one\")" ] }, { @@ -1182,266 +1120,82 @@ "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "{'length': 3, 'width': 4, 'ID': 'this', 'area': 12}" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "area=13.035, perimeter=14.69\n" + ] } ], "source": [ - "rr.calc_area()\n", - "rr.__dict__" + "print(f\"area={r34.calc_area()}, perimeter={r34.calc_perimeter()}\")" ] }, { "cell_type": "markdown", - "id": "e14d6800", + "id": "c746c5e4", "metadata": {}, "source": [ - "Let's make our list of `rectangle` objects again, but use a method to calculate the areas." + "### A note on property methods\n", + "\n", + "Properties are a special method that behaves similar to an attribute. These methods allow for on the fly (\"dynamic\") calculations and variable construction among other things. \n", + "\n", + "Properties are defined with a special decorator (`@property`). Decorators are an advanced topic and won't be covered in this course. More information about decorators, how they are used, and how they work can be found [here](https://betterprogramming.pub/decorators-in-python-72a1d578eac4).\n" ] }, { "cell_type": "code", "execution_count": 43, - "id": "25909c1e", + "id": "e0817eca", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'length': 35, 'width': 25, 'ID': 'rectangle one'}" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "all_rectangles = list()\n", - "all_rectangles.append(Rectangle(35,25,'rectangle one'))\n", - "all_rectangles.append(Rectangle(150, 1000, 'big dog'))\n", - "vars(all_rectangles[0])\n", - "all_rectangles[0].__dict__" + "class Rectangle(object):\n", + " def __init__(self, x, y, ID):\n", + " self.length = x\n", + " self.width = y\n", + " self.ID = ID\n", + " \n", + " @property\n", + " def area(self):\n", + " return self.length * self.width" ] }, { "cell_type": "code", - "execution_count": 44, - "id": "b556938d", + "execution_count": null, + "id": "6a784c63", "metadata": {}, "outputs": [], - "source": [ - "for csqr in all_rectangles:\n", - " csqr.calc_area()" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "e1e8adf7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "rectangle one area=875\n", - "big dog area=150000\n" - ] - }, - { - "data": { - "text/plain": [ - "{'length': 35, 'width': 25, 'ID': 'rectangle one', 'area': 875}" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# let's loop over again to show the area\n", - "for csqr in all_rectangles:\n", - " print(f\"{csqr.ID} area={csqr.area}\")\n", - "vars(all_rectangles[0])" - ] - }, - { - "cell_type": "markdown", - "id": "cf11d87b", - "metadata": {}, - "source": [ - "We could even incorporate calculations into the `__init__` constructor so the area is calculated on instantiation." - ] + "source": [] }, { "cell_type": "code", - "execution_count": 46, - "id": "5a084e14", + "execution_count": null, + "id": "bda89c8e", "metadata": {}, "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.area = x*y\n", - " self.ID = ID" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "9dac1b6a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'length': 4, 'width': 5, 'area': 20, 'ID': 'bummer'}" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rr = Rectangle(4,5,'bummer')\n", - "rr.__dict__" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "e87976ad", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'length': 100, 'width': 5, 'area': 20, 'ID': 'bummer'}" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rr.length = 100\n", - "rr.__dict__" - ] + "source": [] }, { "cell_type": "markdown", - "id": "c746c5e4", + "id": "863db73c", "metadata": {}, "source": [ - "Uh oh! Length changes but the overall area of the rectangle doesn't\n", + "### Just for funsies, let's extend the class to check if we made a golden rectangle\n", "\n", - "### Properties to the rescue!!!\n", + "In mathematics, the golden ratio between two values occurs if their ratio is the same as the ratio of their sum over the larger of the two quantities. We can represent that as:\n", "\n", - "Properties are a special method that behaves similar to an attribute. These methods allow for on the fly (\"dynamic\") calculations and variable construction among other things. \n", + "$\\frac{a + b}{a} = \\frac{a}{b}$\n", "\n", - "Properties are defined with a special decorator (`@property`). Decorators are an advanced topic and won't be covered in this course. More information about decorators, how they are used, and how they work can be found [here](https://betterprogramming.pub/decorators-in-python-72a1d578eac4).\n" + "For a rectangle:\n", + " - a = shorter side\n", + " - b = longer side - shorter side\n" ] }, { "cell_type": "code", - "execution_count": 49, - "id": "e0817eca", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "6a784c63", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'length': 5, 'width': 9, 'ID': 'awesome rectangle'}\n", - "45\n" - ] - } - ], - "source": [ - "rect = Rectangle(5, 9, \"awesome rectangle\")\n", - "print(rect.__dict__)\n", - "print(rect.area)" - ] - }, - { - "cell_type": "markdown", - "id": "5cceaada", - "metadata": {}, - "source": [ - "what happens if the length of the rectangle grows?" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "bda89c8e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ID=awesome rectangle, length=5, width=9, area=45\n", - "ID=awesome rectangle, length=16, width=9, area=144\n", - "ID=awesome rectangle, length=27, width=9, area=243\n", - "ID=awesome rectangle, length=38, width=9, area=342\n", - "ID=awesome rectangle, length=49, width=9, area=441\n", - "ID=awesome rectangle, length=60, width=9, area=540\n", - "ID=awesome rectangle, length=71, width=9, area=639\n", - "ID=awesome rectangle, length=82, width=9, area=738\n", - "ID=awesome rectangle, length=93, width=9, area=837\n" - ] - } - ], - "source": [ - "for new_length in range(5, 100, 11):\n", - " rect.length = new_length\n", - " print(f\"ID={rect.ID}, length={rect.length}, width={rect.width}, area={rect.area}\")" - ] - }, - { - "cell_type": "markdown", - "id": "863db73c", - "metadata": {}, - "source": [ - "### Test your skills!!!\n", - "\n", - "build a `Rectangle` class that includes an `area` and `perimiter_length` property method.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 52, + "execution_count": 44, "id": "c8b0994e", "metadata": {}, "outputs": [], @@ -1458,460 +1212,53 @@ " \n", " @property\n", " def perimeter(self):\n", - " return 2 * (self.length + self.width)" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "id": "6c9ca452", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "22" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r = Rectangle(5, 6, \"f\")\n", - "r.perimeter" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c808fb3a", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "cd6ab207", - "metadata": {}, - "source": [ - "## Operator and Special Method Overloading*\n", - "\n", - "One special thing we can do is overload operators. This means we can customize the behavior that an object (as an instance of a class) will exhibit when called. The `__init__` constructor was an example of this which we have done something similar already. When an instance is made from a class, whatever is defined in `__init__` is performed as part of creating the instance which is effectively _overloading_ the special method `__init__`.\n", - "\n", - "Another really common example is `__str__`. This function provides a string for Python to display when `print` of `str` is called on an object.\n", - "\n", - "Looking at the example of `rectangle` objects we used above. \n", + " return 2 * (self.length + self.width)\n", "\n", - "\\*N.B. --> The concept of `overloading` is different in Python than in FORTRAN. Also, there appears to be a lack of precision about the use of the terms `overload` and `override`. I'm using the terminology from the _O' Reilly_ book here, but note that you might find other people using `override` instead. The concept in this case is the same." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "id": "c509dd88", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __str__(self):\n", - " return f\"I'm a Rectangle and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "b605e6ab", - "metadata": {}, - "outputs": [], - "source": [ - "rect = Rectangle(3, 4, 'super_awesome_rectangle')" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "id": "1f6897a1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "<__main__.Rectangle at 0x111a3f990>" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rect" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "id": "e5bfa9aa", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I'm a Rectangle and my name is super_awesome_rectangle, length=3, width=4, area=12\n" - ] - } - ], - "source": [ - "print(rect)" - ] - }, - { - "cell_type": "markdown", - "id": "4a399ca3", - "metadata": {}, - "source": [ - "Notice that this prints out a default string telling us the `rectangle` is an object in this code (e.g. under `__main__`). But, we can _overload_ and provide a more useful return string using `__repr__`. This serves the same purpose as `__str__` but also returns the string in other circumstances.\n", + " def is_golden(self):\n", + " if self.length > self.width:\n", + " a = self.width\n", + " b = self.length - self.width\n", + " else:\n", + " a = self.length\n", + " b = self.width - self.length\n", "\n", - "What's the difference between `__repr__` and `__str__`? The Diet Mountain Dew crew has discussed this [here](http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python)" + " if abs(((a + b) / a) - (a / b)) < 0.01:\n", + " return True\n", + " else:\n", + " return False" ] }, { "cell_type": "code", - "execution_count": 58, - "id": "d990d460", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __repr__(self):\n", - " return f\"I'm a Rectangle and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "id": "739ab614", - "metadata": {}, - "outputs": [], - "source": [ - "rect = Rectangle(3, 4, 'super_awesome_rectangle')" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "id": "4395cbc9", + "execution_count": 45, + "id": "6c9ca452", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "I'm a Rectangle and my name is super_awesome_rectangle, length=3, width=4, area=12" + "True" ] }, - "execution_count": 60, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "rect" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "18770648", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I'm a Rectangle and my name is super_awesome_rectangle, length=3, width=4, area=12\n" - ] - } - ], - "source": [ - " print(rect)" - ] - }, - { - "cell_type": "markdown", - "id": "f5157b04", - "metadata": {}, - "source": [ - "We can even overload other operators like `__add__` which will control what happens when this object is added to another.\n", - "\n", - "A complete list of which special methods and operators can be overloaded is found [here](https://docs.python.org/3.9/reference/datamodel.html#special-method-names).\n", - "\n", - "A short list of standard operators for mathematical functions can be found [here](https://docs.python.org/3/library/operator.html)." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "id": "faee0ac8", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __add__(self, other):\n", - " print(\"what should we add today???\")" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "id": "4dd29c4c", - "metadata": {}, - "outputs": [], - "source": [ - "r1 = Rectangle(1, 1, \"Small rect\")\n", - "r2 = Rectangle(3, 4, \"bigger one\")" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "id": "9b2287c2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "what should we add today???\n" - ] - } - ], - "source": [ - "r1 + r2" - ] - }, - { - "cell_type": "markdown", - "id": "99cda1ee", - "metadata": {}, - "source": [ - "### Test your skills -- overload `__add__` so that adding two rectangles adds their area\n", - "\n", - "Start with the definition we just made. HINT: you will need to represent the other object with `other`." - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "id": "e66649f1", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __add__(self, other):\n", - " print(self.add + other.add)\n", - " return self.add + other.add" + "golden = Rectangle(18.52, 30, \"is_it_golden?\")\n", + "golden.is_golden" ] }, { "cell_type": "code", "execution_count": null, - "id": "7d28debd", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa85c7cb", + "id": "c808fb3a", "metadata": {}, "outputs": [], "source": [] }, - { - "cell_type": "markdown", - "id": "0c9b932a", - "metadata": {}, - "source": [ - "Operator overloading is very powerful, but with great power comes great responsibility. Use with caution." - ] - }, - { - "cell_type": "markdown", - "id": "d043f1c5", - "metadata": {}, - "source": [ - "## Object-oriented programming (OOP)\n", - "\n", - "In the O'Reilly book _Learning Python, 5th Edition_ is a great discussion about Object-Oriented Programming. The author makes this distinction that much of what we are doing with Python is _object-based_ but to truly be object-oriented, we need to also use something called inheritance." - ] - }, - { - "cell_type": "markdown", - "id": "26ee23fd", - "metadata": {}, - "source": [ - "## Inheritance\n", - "Let's revisit our class for rectangles without the overloading of `__add__`" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "id": "4e0a08b5", - "metadata": {}, - "outputs": [], - "source": [ - "class Rectangle(object):\n", - " def __init__(self, x, y, ID):\n", - " self.length = x\n", - " self.width = y\n", - " self.ID = ID\n", - " self._type = \"Rectangle\"\n", - " \n", - " @property\n", - " def area(self):\n", - " return self.length * self.width\n", - " \n", - " def __repr__(self):\n", - " return f\"I'm a {self._type} and my name is {self.ID}, \" \\\n", - " f\"length={self.length}, width={self.width}, area={self.area}\"\n", - " " - ] - }, - { - "cell_type": "markdown", - "id": "3176c420", - "metadata": {}, - "source": [ - "We can _inherit_ these characteristics (the methods and properties) in a new kind of class that has a custom bit of functionality. Say we would like to create a second class that is specifically for representing squares.\n", - "\n", - "We can redefine a new class _inheriting_ the rectangle attributes and methods. We can even add the new functionality on top of it.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "id": "88f1aecd", - "metadata": {}, - "outputs": [], - "source": [ - "# Rectangle is the parent class\n", - "class Square(Rectangle):\n", - " \"\"\"\n", - " Doc strings for Square\n", - " \"\"\"\n", - " def __init__(self, x, ID):\n", - " # initialize the parent class using the super().__init__ call\n", - " super().__init__(x, x, ID)\n", - " self._type = \"Square\"\n", - " \n", - " @property\n", - " def perimiter(self):\n", - " return 2 * (self.length + self.width)\n", - " \n", - " def __repr__(self):\n", - " # get the __repr__ from Rectangle using super()\n", - " s = super().__repr__()\n", - " s += f\", perimiter={self.perimiter}\"\n", - " return s" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "id": "06b6c621", - "metadata": {}, - "outputs": [], - "source": [ - "# live code example\n", - "rect = Rectangle(2, 4, \"Tony\")\n", - "square = Square(5, \"Ravioli\") " - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "id": "c9b5763f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "I'm a Rectangle and my name is Tony, length=2, width=4, area=8" - ] - }, - "execution_count": 69, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rect" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "id": "e1cedb0d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "I'm a Square and my name is Ravioli, length=5, width=5, area=25, perimiter=20" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "square" - ] - }, { "cell_type": "markdown", "id": "b0f95e24", @@ -1925,12 +1272,12 @@ "\n", "Packages are collections of modules and are often \"installed\" to be accessible to Python from anywhere. More on that at the end of the lesson.\n", "\n", - "Let's import a module and find a function within it." + "Let's import a module like `random` and find a function within it." ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 46, "id": "3917f949", "metadata": {}, "outputs": [], @@ -1941,7 +1288,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 47, "id": "ef083f54", "metadata": {}, "outputs": [ @@ -1951,7 +1298,7 @@ "" ] }, - "execution_count": 72, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1962,17 +1309,17 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 48, "id": "2a77a0ca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "0.8012558485047404" + "0.28743741543767465" ] }, - "execution_count": 73, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1994,17 +1341,17 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 49, "id": "76f3cfea", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 74, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -2026,7 +1373,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 50, "id": "3869cf46", "metadata": {}, "outputs": [], @@ -2051,7 +1398,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 51, "id": "8036d7bb", "metadata": {}, "outputs": [ @@ -2061,7 +1408,7 @@ "" ] }, - "execution_count": 76, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -2073,7 +1420,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 52, "id": "4c3bbe5d", "metadata": {}, "outputs": [ @@ -2091,7 +1438,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 53, "id": "27ba4afb", "metadata": {}, "outputs": [ @@ -2101,7 +1448,7 @@ "2.23606797749979" ] }, - "execution_count": 78, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -2120,7 +1467,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 54, "id": "30af8923", "metadata": {}, "outputs": [ @@ -2130,7 +1477,7 @@ "" ] }, - "execution_count": 79, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -2150,7 +1497,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 55, "id": "cead1086", "metadata": {}, "outputs": [ @@ -2160,7 +1507,7 @@ "" ] }, - "execution_count": 80, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -2180,7 +1527,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 56, "id": "9b8f5cc7", "metadata": {}, "outputs": [ @@ -2194,7 +1541,7 @@ " )" ] }, - "execution_count": 81, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -2216,7 +1563,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 57, "id": "36d33631", "metadata": {}, "outputs": [], @@ -2241,7 +1588,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 58, "id": "26316c95", "metadata": {}, "outputs": [ @@ -2296,27 +1643,10 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": null, "id": "ea1f6fde", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/Users/mnfienen/Documents/GIT/python-for-hydrology/notebooks/part0_python_intro/solutions',\n", - " '/usr/local/condor/lib/python3',\n", - " '/Users/mnfienen/miniforge3/envs/pyclass/lib/python311.zip',\n", - " '/Users/mnfienen/miniforge3/envs/pyclass/lib/python3.11',\n", - " '/Users/mnfienen/miniforge3/envs/pyclass/lib/python3.11/lib-dynload',\n", - " '',\n", - " '/Users/mnfienen/miniforge3/envs/pyclass/lib/python3.11/site-packages']" - ] - }, - "execution_count": 84, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import sys\n", "sys.path" @@ -2331,14 +1661,14 @@ "\n", "In this exercise we'll create our first module and import it into this notebook.\n", "\n", - "Open the python file \"exercise_xx.py\" in an IDE or text file and create a class called `Circle`. Inputs to circle should be a radius and ID. Include in the `Circle` class a way to the calculate area and the circumference. After building the class, try importing into this notebook and using the it.\n", + "Open the python file \"circle_module.py\" in an IDE or text file and create a class called `Circle`. Inputs to circle should be a radius and ID. Include in the `Circle` class a way to the calculate area and the circumference. After building the class, try importing into this notebook and using the it.\n", "\n", "**Bonus exercise:** Find a way to make the `Circle` objects divisible and compare the difference in area between a 12\" and 14\" pizza.\n" ] }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 60, "id": "d11191c4", "metadata": {}, "outputs": [], @@ -2348,7 +1678,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 61, "id": "80a24c3b", "metadata": {}, "outputs": [ @@ -2408,7 +1738,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.14.6" } }, "nbformat": 4,