{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "###### Content under Creative Commons Attribution license CC-BY 4.0, code under MIT license (c)2015 G.F. Forsyth." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Python Names" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We introduced functions way back in Module 1 and they have served us well (and will continue to do so!). There are a few behaviors that bear closer examination before we dive too deeply into the next lessons. These are all common Python gotchas! that will continue to rear their heads, so it's better that you know about them now to avoid a large amount of hair-pulling when they crop up. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a [fantastic talk](https://www.youtube.com/watch?v=_AEJHKGk9ns) from PyCon 2015 by Ned Batchelder on YouTube that will walk you through all of the things below and more, but we'll also include a short summary of some of the issues." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Assigning variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we want a variable `x` to have a value of 5, we assign the *name* `x` to the *value* `5`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n" ] } ], "source": [ "x = 5\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Integers are immutable. They don't change. If we assign another name `y` to be equal to `x`, there is no operation we can perform on `y` that will change `x` (or the 5). " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "5\n" ] } ], "source": [ "y = x\n", "print(y)\n", "print(x)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6\n", "5\n" ] } ], "source": [ "y += 1\n", "print(y)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some datatypes *are* mutable, however, and that's where the trouble can start. If instead of an integer, `x` points to a list (lists are mutable), then things are different." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 5]\n" ] } ], "source": [ "x = [1, 2, 3]\n", "y = x\n", "y.append(5)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happened? We created a list `[1, 2, 3]` and pointed the name `x` at it. Then we pointed the name `y` at the *same* list. When we add a value to the list, there is only the one list, so the changes to it are reflected whether we ask for it by its first name, `x`, or its second name, `y`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What does this have to do with functions?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A reasonable question. When you call a function and send it a few names (inputs), that action doesn't create copies of the objects that those names point to. It just creates a *new* name that points at the *same* data.\n", "\n", "Let's create a simple function that adds a value to a list and then returns a \"copy\" of that list." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def add_to_list(mylist):\n", " mylist.append(7)\n", " \n", " newlist = mylist.copy()\n", " \n", " return newlist" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "mylist = [1, 2, 3]\n", "newlist = add_to_list(mylist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We send in `mylist`, make a change to it, then make a copy of it and return the copy. But we didn't return `mylist` so those changes are discarded, right?" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 7]\n" ] } ], "source": [ "print(newlist)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 7]\n" ] } ], "source": [ "print(mylist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wrong. We sent in the name `mylist` and then appended a value to it. At that point, the list has been changed. We used the `copy()` command to create `newlist`, so it points to a different list than `mylist`, but `mylist` has still been altered by the function. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What if we change the names?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Is this because the function expects a list named `mylist` and that is what we sent? Alas, no. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2, 4, 2, 7]\n" ] } ], "source": [ "T = [2, 4, 2]\n", "newlist = add_to_list(T)\n", "print(T)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we send the name `T` to the function `add_to_list`, the function creates the new name `mylist` and points it to the same list that `T` points to." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What do we do?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The most important thing is to be aware of this behavior. It's a feature of the language and it doesn't often cause problems, but you need to know about it for when it does cause problems. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.display import HTML\n", "css_file = '../../styles/numericalmoocstyle.css'\n", "HTML(open(css_file, 'r').read())" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (MOOC)", "language": "python", "name": "py36-mooc" }, "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.6.5" } }, "nbformat": 4, "nbformat_minor": 1 }