Skip to content

Instantly share code, notes, and snippets.

@aganders3
Created June 25, 2015 17:34
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save aganders3/1b725f134873bbf71611 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "# Mutable vs. Immutable Objects \n<br />\n### DesertPy Lightning Talk, June 2015\n<br />\n### Ashley Anderson\n[@aganders3](twitter.com/aganders3) on Twitter or aganders3@gmail.com"
},
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "## Everything is an object"
},
{
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"cell_type": "markdown",
"source": "Every Python object has a type, identity, and *value*.\n\nThe builtin function `type()` returns a `type` object. \nThe builtin function `id()` returns the object's identity (an integer). \nValue is kind of vague, though."
},
{
"metadata": {
"scrolled": true,
"collapsed": false,
"slideshow": {
"slide_type": "-"
},
"trusted": true
},
"cell_type": "code",
"source": "print 1, type(1), id(1)\n\nfoo = range(10)\nprint foo, type(foo), id(foo)\n\nimport sys \nprint sys, type(sys), id(sys)\n\ndef bar():\n pass\n\nprint bar, type(bar), id(bar)",
"execution_count": 169,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "1 <type 'int'> 4299197592\n[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <type 'list'> 4415085312\n<module 'sys' (built-in)> <type 'module'> 4297509808\n<function bar at 0x10728b320> <type 'function'> 4415075104\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "## Mutability is about *values*\n\nThink of an object's value as any and all of its attributes. For example, anything you'd type `self.` to get at from inside if you wrote the class yourself.\n\nMost custom classes are mutable. To create a simple immutable class, try using a [`namedtuple`](https://docs.python.org/2/library/collections.html#collections.namedtuple). Otherwise you can raise a `TypeError` from `__setattr__` to make your own immutable type."
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "### Immutable objects\nYou can't change their values. \nThe value of `x` is the same before and after `foo` is called.\n\n x = some_immutable\n foo(x)\n print x\n\nThis is *kind of* like pass-by-value.\n\nImmutable objects include **numbers**, **strings**, **tuples**."
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "def add_one(bar):\n bar += 1\n\nb = 5\nprint b, id(b)\nadd_one(b)\nprint b, id(b)",
"execution_count": 120,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "5 4299197496\n5 4299197496\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "### Mutable objects\nYou can change their values. \nThe value of `y` *may be different* after `foo` is called.\n\n y = some_mutable\n foo(y)\n print y\n \nThis is *kind of* like pass-by-reference.\n\nMutable objects include **lists**, **dictionaries**, etc."
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "def reverse_list(foo):\n foo.reverse()\n\na = range(10)\nprint a, id(a)\nreverse_list(a)\nprint a, id(a)",
"execution_count": 121,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 4415096664\n[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 4415096664\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "## But, wait...\nIt's pretty easy to change the value of a number variable..."
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "foo = 42\nprint foo\n\nfoo += 1\nprint foo\n\nfoo = 42\nprint foo",
"execution_count": 151,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "42\n43\n42\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "## Nah...\nThe assignment operator doesn't change the value of an object, it changes its identity."
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "-"
},
"trusted": true
},
"cell_type": "code",
"source": "foo = 42\nprint foo, id(foo)\nfoo += 1\nprint foo, id(foo)\nfoo = \"bar\"\nprint foo, id(foo)",
"execution_count": 148,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "42 4299198584\n43 4299198560\nbar 4381029040\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "## Values get confusing when we have containers\n\nIf you have an immutable container (e.g. a tuple) that holds mutable objects (e.g. lists), you can still mutate the contained objects without mutating the container."
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "-"
},
"trusted": true
},
"cell_type": "code",
"source": "A = ([],[],[])\nprint A, id(A[0]), id(A)\nA[0].append(5)\nprint A, id(A[0]), id(A)\nA[1] = [5]",
"execution_count": 171,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "([], [], []) 4414903504 4414893984\n([5], [], []) 4414903504 4414893984\n"
},
{
"ename": "TypeError",
"evalue": "'tuple' object does not support item assignment",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-171-868c79a8f9fc>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mA\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment"
],
"output_type": "error"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"cell_type": "markdown",
"source": "## Gotcha!\nMostly you don't have to think about this stuff, but sometimes you do."
},
{
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "def my_appending_function(element, initial_list=[]):\n # do some stuff\n initial_list.append(element)\n return initial_list",
"execution_count": 1,
"outputs": []
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "print my_appending_function((1,2,3,5), range(2))\nprint my_appending_function(10)\nprint my_appending_function(5)",
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "[0, 1, (1, 2, 3, 5)]\n[10]\n[10, 5]\n"
}
]
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "## Mutable objects as default arguments\nThese are defined *once* when the function is defined. This is a result of [`def` being an *executable statement* that binds the name of the function to the function object](http://effbot.org/zone/default-values.htm). All this has something to do with Python functions being \"first class\" objects."
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "## Mutable objects as deafault arguments\nSo...instead of this:"
},
{
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
},
"trusted": true
},
"cell_type": "code",
"source": "def my_appending_function(element, initial_list=[]):\n # do some stuff\n initial_list.append(element)\n return initial_list",
"execution_count": 165,
"outputs": []
},
{
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"cell_type": "markdown",
"source": "Do this:"
},
{
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
},
"trusted": true
},
"cell_type": "code",
"source": "def my_appending_function(element, initial_list=None):\n if initial_list is None:\n initial_list = []\n # do some stuff\n initial_list.append(element)\n return initial_list",
"execution_count": 166,
"outputs": []
},
{
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"cell_type": "markdown",
"source": "## Mutable objects as default arguments\n\nNow it does what you (may have) expected:"
},
{
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "def my_appending_function(number, initial_list=None):\n if initial_list is None:\n initial_list = []\n # do some stuff\n initial_list.append(number)\n return initial_list",
"execution_count": 77,
"outputs": []
},
{
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
},
"trusted": true
},
"cell_type": "code",
"source": "print my_appending_function(10)\nprint my_appending_function(5)\nprint my_appending_function(10)\nprint my_appending_function(5)",
"execution_count": 167,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "[10]\n[5]\n[10]\n[5]\n"
}
]
}
],
"metadata": {
"kernelspec": {
"name": "python2",
"display_name": "Python 2",
"language": "python"
},
"language_info": {
"mimetype": "text/x-python",
"nbconvert_exporter": "python",
"name": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10",
"file_extension": ".py",
"codemirror_mode": {
"version": 2,
"name": "ipython"
}
},
"celltoolbar": "Slideshow"
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment