{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Variables v Objects Exercises\n", "\n", "To get used to thinking about the distinction between variables and objects, we're going to look at some Python code and *write out* what would be happening in Python were we to run the code. \n", "\n", "To illustrate, consider teh following block of code:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "# Make a new list\n", "x = [1, 2, 3]\n", "# Make new var Y, and assign it x. In R this would make a copy.\n", "y = x\n", "# Add to the end of the string\n", "x.append(4)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we were to draw out how this code is being executed, we would want to draw three figures, one for each line of code, like this: \n", "\n", "`x = [1, 2, 3]`:\n", "\n", "![vars_v_objects_1](images/vars_v_objects_1.png)\n", "\n", "`y = x`:\n", "\n", "![vars_v_objects_2](images/vars_v_objects_2.png)\n", "\n", "`x.append(4)`:\n", "\n", "![vars_v_objects_3](images/vars_v_objects_3.png)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we could write a dictionary as:\n", "\n", "![vars_v_objects_dict](images/vars_v_objects_dict.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To help complete these exercises, you can print out copies of [code-drawing templates here](code_drawing_template.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 1\n", "\n", "Using the space below, write out, one line at a time, the execution of the following block of code which first creates a dictionary will the names of all the animals in my very small (and weird) zoo:\n", "\n", "```python\n", "animals_in_my_zoo = {\n", " \"Zebras\": [\"Stripey\", \"Black and White\"],\n", " \"Polar Bears\": \"Fluffy\",\n", " \"Dinosaurs\": \"Bitey\",\n", "}\n", "x = animals_in_my_zoo\n", "\n", "x[\"Meerkat\"] = \"Peeps\"\n", "```\n", "Now, given this code, what would the value of `animals_in_my_zoo[\"Meerkat\"]` be?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you've gotten an answer, open up Python and run the code to see if you were correct. If not, can you tell why?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 2\n", "\n", "Now let's do a few list manipulations. For these manipulations, you may need to open Python to check the documentation for different functions to figure out whether they mutate the list in-place, or whether they create a new object. \n", "\n", "```python\n", "x = [1, 2, 3, 4]\n", "y = x\n", "x.pop(1)\n", "```\n", "\n", "Sketch out this code, and make a prediction for the value of `y`. Then run the code in Python to check your answer. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 3\n", "\n", "Now sketch this code. Again, use Python to check the documentation for `sorted` if you aren't sure if it mutates or not.\n", "\n", "```python\n", "x = [1, 4, 3, 2]\n", "y = x\n", "sorted(x)\n", "```\n", "\n", "Sketch out this code, and make a prediction for the value of `x` and the value of `y`. Then run the code in Python to check your answer. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 4\n", "\n", "```python\n", "x = [1, 4, 3, 2]\n", "y = x\n", "x = x.sort()\n", "```\n", "\n", "Now sketch out this code, and make a prediction for the value of `x` and the value of `y`. Then run Python to check your answer. \n", "\n", "If the result surprises you, you can find some [discussion of what likely happened here](Discussion_vars_v_objects.ipynb#Return-Values-and-Object-Mutations). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 5\n", "\n", "One of the thing that's very useful about lists, dictionaries, and sets in Python is that they are *recursive*, meaning a list can contain a list. While powerful, though, this can also be tricky! Let's draw out the following code:\n", "\n", "```python\n", "x = [3.14, 42]\n", "z = {\"boring numbers\": [1, 2, 3], \"interesting numbers\": x}\n", "y = x\n", "y.append(1.618)\n", "```\n", "\n", "Now, what is the value of `z[\"interesting numbers\"]`?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 6\n", "\n", "As noted in our tutorial, in general, almost all collections of things in Python are mutable with the exception of `tuples`. Tuples are, quite simply, immutable lists. Let's play a little with tuples. \n", "\n", "Sketch the following code: \n", "\n", "```python \n", "x = [42, -1]\n", "y = (1, 2, x)\n", "x.append(-2)\n", "```\n", "\n", "Write down what you think will be the values of `x` and `y`? Now check your answers in Python. \n", "\n", "[If you got this wrong, you can find discussion of this result here](Discussion_vars_v_objects.ipynb#tuples)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `copy` versus `deepcopy`\n", "\n", "One of the challenges of the ability of Python collections to hold other collections is that copying can be a little tricky. Consider the following code:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "x = [42, 3.14, [1, 2, 3]]\n", "y = x.copy()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Naturally, we might think that `x` and `y` are now fully independent, but it turns out this is not the case. The reason is that `copy` copied the *arrow* from our first list (42, 3.14) to the second list (1, 2, 3), but it did *not* create an entirely new copy of the second list (`[1, 2, 3]`). \n", "\n", "In other words, it copied the *object reference* in the first list, not the referenced object itself, which looks like this:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![recursive_copy](images/recursive_copy.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus if we modify this second list through `x`, that change will also be evident in `y`!" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[42, 3.14, [1, 2, 3, 'where did this come from?!']]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y[2].append(\"where did this come from?!\")\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want not just copy our object references, but also all the referenced objects, we need to do a \"deep copy\":" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import copy\n", "\n", "y = copy.deepcopy(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which will generate this situation:\n", "\n", "![recursive_deepcopy](images/recursive_deepcopy.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sketch the following code: \n", " \n", "```python\n", "x = {\"dog_names\": [\"fido\", \"woofer\"], \"cat_names\": [\"fluffy\", \"bite-y\"]}\n", "y = x.copy()\n", "y[\"dinosaur_names\"] = [\"big guy\", \"super lizard\"]\n", "y[\"cat_names\"].append(\"tiger\")\n", "```\n", "\n", "What would you get if you ran `x[\"dinosaur_names\"]`?\n", "\n", "What would you get if you ran `x[\"cat_names\"]`?\n", "\n", "Check your answers with Python." ] } ], "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.10.8" } }, "nbformat": 4, "nbformat_minor": 4 }