{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1e5acde4-4ac1-4600-a486-e28be3ece303",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "toc-hr-collapsed": true
   },
   "source": [
    "# Python Objects & References\n",
    "\n",
    "Everything in Python is an `object`.  An object is a piece of memory with values & associated operations.\n",
    "\n",
    "We've seen plenty of methods on `str`, `list`, `dict`, etc. \n",
    "\n",
    "`a_string.lower()`, `dict.pop(val)`, etc.\n",
    "\n",
    "As we'll see in time, every type in Python works this way. \n",
    "\n",
    "Operators like `+`, `-`, `and` and `or` are \"associated operations\" when we're using scalars like `int` or `bool`."
   ]
  },
  {
   "attachments": {
    "shared_ref1.png": {
     "image/png": "[encoded data, 265252 characters]"
    }
   },
   "cell_type": "markdown",
   "id": "3ac96fe3-33ee-4648-a174-16886a78bb0e",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Variables in python are referred to as *names* or *identifiers*.\n",
    "\n",
    "![shared_ref1.png](attachment:shared_ref1.png)\n",
    "\n",
    " -- Learning Python 2013\n",
    " \n",
    "A name does not uniquely identify an object!"
   ]
  },
  {
   "attachments": {
    "shared_ref2.png": {
     "image/png": "[encoded data, 387812 characters]"
    }
   },
   "cell_type": "markdown",
   "id": "802b0f51-afb9-4c7f-8f56-6380c81f658d",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## objects are typed, not variables\n",
    "\n",
    "![shared_ref2.png](attachment:shared_ref2.png)\n",
    " -- Learning Python 2013"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f499594-559b-447e-b0a1-40b81b583913",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Shared references\n",
    "\n",
    "Setting a variable to a new value does not alter the original.\n",
    "\n",
    "It causes the variable to reference a brand new object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed9dd65a-5792-4340-a354-0bb10b2828b3",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "x = 10\n",
    "y = x\n",
    "x = 20\n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab5bbf92-9b1a-4159-a9ce-4a9f1bea7785",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# what does this mean for mutable objects?\n",
    "x = [1, 2, 3]\n",
    "y = x\n",
    "y.append(4)\n",
    "print(x)\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8410e5b-f231-48df-8cef-7c947f6945fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 3\n",
    "b = a\n",
    "a *= 2\n",
    "print(a, b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc471c6f-e4ac-4c15-84b1-e613644f066e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "a = 3113\n",
    "id(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75f6025a-4295-4a17-b08b-abc519bd24c4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "b = 39209328\n",
    "id(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "927231d8-4f4c-47e5-8016-dbc6c436325a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed396d6b-fad3-4c43-9412-dbd9db181410",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3cb6f668-817f-4a37-aa08-253139f17ebb",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "c = b\n",
    "id(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02a94a49-a376-4879-9004-527140dd7059",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(\"hello\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ce3f035-dd03-49e9-9f4e-a14947eb377c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(\"hello\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72893a54-b91d-43a6-bb78-3b19719e53d8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "x = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b11c23b9-b511-45ef-a1c8-1bf2eb3e48e1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "y = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ccf72016-cac0-469e-a1ff-7a8065b78e44",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c61cc294-14f9-4513-b160-3246efe0d501",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "id(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99744fac-c1d9-4532-a7d4-0dcbc1a0d2a3",
   "metadata": {},
   "source": [
    "## Garbage Collection\n",
    "\n",
    "Python is a garbage collected language.  \n",
    "\n",
    "We don't free our own memory, Python does instead.\n",
    "\n",
    "Behind the scenes, Python stores a reference counter on each `object`.  How many names/objects reference the object.\n",
    "\n",
    "When reference count drops to zero, Python can reclaim the memory."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fe201ab-1d9e-441a-b90c-27ca22da2e53",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Identity\n",
    "\n",
    "The built-in `id(...)` function returns the identity of an object, which is an integer value guaranteed to be unique and constant for lifetime of object\n",
    "\n",
    "In the ofificial (\"CPython\") Interpeter we are using in this class, it is the address of the memory location storing the object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ec197a99-5b09-480e-b7aa-8129474e03d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4384777776\n"
     ]
    }
   ],
   "source": [
    "x = \"MPCS\" \n",
    "print(id(x))  # Unique integer-value for the object pointed by x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "68e3ade8-d9a1-4d9c-b2f6-382fb90e745a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4384777776\n"
     ]
    }
   ],
   "source": [
    "y = \"MPCS\" \n",
    "print(id(y)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "93b42c37-41d5-41ff-a92e-863bcf2ee78e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fruit1 id = 4384803008 \n",
      " Fruit2 id = 4384782464\n",
      "Fruit3 id= 4384782464\n"
     ]
    }
   ],
   "source": [
    "fruit1 = (\"Apples\", 4)\n",
    "fruit2 = (\"Apples\", 4)\n",
    "fruit3 = fruit2\n",
    "print(f\"Fruit1 id = {id(fruit1)} \\n Fruit2 id = {id(fruit2)}\")\n",
    "print(f\"Fruit3 id= {id(fruit3)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "198a8eea-56d0-4402-baeb-0efe54486630",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fruit is fruit2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f36d4ec8-eefa-4474-84b1-b8be365329bc",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Equality vs. Identity\n",
    "\n",
    "Two different ways of testing if objects are the \"same\":\n",
    "\n",
    "- Equality operator (`==`): Returns true if two objects are equal (i.e., have the same value)\n",
    "- Identity operator (`is`): Returns true if two objects identities are the same.\n",
    "\n",
    "`a is b` means `id(a) == id(b)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "67ee08d9-96b4-426f-bc05-4dc7bfecdf96",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a == b True\n",
      "4384871424\n",
      "4384871168\n",
      "a is b False\n"
     ]
    }
   ],
   "source": [
    "a = [1, 2, 3]\n",
    "b = [1, 2, 3]\n",
    "print(\"a == b\", a == b)\n",
    "\n",
    "print(id(a))\n",
    "print(id(b))\n",
    "print(\"a is b\", a is b)  # The id values are different"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "1479f1f1-3d10-41c5-ac1b-30f5b9c4cef1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4366718832\n"
     ]
    }
   ],
   "source": [
    "print(id(None))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "0c9d9d98-2bd2-41b9-aee8-d5cf68f76c21",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4366718832"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def f():\n",
    "    pass\n",
    "id(f())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37ad8b3a-2b9b-4879-afb2-a05cc3ab659e",
   "metadata": {},
   "source": [
    "#### `is None`\n",
    "\n",
    "If you ever need to check if a value is `None`, you'd use `is None` or `is not None`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a48460e-a342-4d18-8383-b84483a3a010",
   "metadata": {},
   "source": [
    "### list / string mutability revisited"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "3f65348f-9118-4840-ab30-6ef05e7dbf93",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4384869248\n",
      "[1, 2, 3, 4]\n",
      "4384869248\n"
     ]
    }
   ],
   "source": [
    "# list d\n",
    "d = [1, 2, 3]\n",
    "print(id(d))\n",
    "d.append(4)\n",
    "print(d)\n",
    "print(id(d))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7d1b2659-239c-4333-ac62-8533406bb025",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4384746736\n",
      "Hello World\n",
      "4384771632\n"
     ]
    }
   ],
   "source": [
    "# str D\n",
    "s = \"Hello\"\n",
    "print(id(s))\n",
    "s += \" World\"\n",
    "print(s)\n",
    "\n",
    "# did s change?\n",
    "print(id(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c056ac96-58bc-41da-8014-443dc10485f6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Aside: Object Creation Quirk\n",
    "\n",
    "    Each time you generate a new value in your script by running an expression, Python creates a new object (i.e., a chunk of memory) to represent that value.\n",
    "    \n",
    "-- Learning Python 2013\n",
    "\n",
    "Not quite! CPython does not guarantee this, and in fact sometimes caches & reuses immutable objects for efficiency.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "f01f68c9-927c-4076-a967-3b6fbbf90033",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "a = 100\n",
    "b = 100\n",
    "\n",
    "# Two different objects, two different ids.\n",
    "print(a is b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "150da8f9-213f-4fc7-9f47-78c33b38d169",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "500 True\n",
      "501 True\n",
      "502 True\n",
      "503 True\n",
      "504 True\n",
      "505 True\n",
      "506 True\n",
      "507 True\n",
      "508 True\n",
      "509 True\n",
      "510 True\n",
      "511 True\n",
      "512 True\n",
      "513 True\n",
      "514 True\n",
      "515 True\n",
      "516 True\n",
      "517 True\n",
      "518 True\n",
      "519 True\n",
      "520 True\n",
      "521 True\n",
      "522 True\n",
      "523 True\n",
      "524 True\n",
      "525 True\n",
      "526 True\n",
      "527 True\n",
      "528 True\n",
      "529 True\n",
      "530 True\n",
      "531 True\n",
      "532 True\n",
      "533 True\n",
      "534 True\n",
      "535 True\n",
      "536 True\n",
      "537 True\n",
      "538 True\n",
      "539 True\n",
      "540 True\n",
      "541 True\n",
      "542 True\n",
      "543 True\n",
      "544 True\n",
      "545 True\n",
      "546 True\n",
      "547 True\n",
      "548 True\n",
      "549 True\n",
      "550 True\n",
      "551 True\n",
      "552 True\n",
      "553 True\n",
      "554 True\n",
      "555 True\n",
      "556 True\n",
      "557 True\n",
      "558 True\n",
      "559 True\n",
      "560 True\n",
      "561 True\n",
      "562 True\n",
      "563 True\n",
      "564 True\n",
      "565 True\n",
      "566 True\n",
      "567 True\n",
      "568 True\n",
      "569 True\n",
      "570 True\n",
      "571 True\n",
      "572 True\n",
      "573 True\n",
      "574 True\n",
      "575 True\n",
      "576 True\n",
      "577 True\n",
      "578 True\n",
      "579 True\n",
      "580 True\n",
      "581 True\n",
      "582 True\n",
      "583 True\n",
      "584 True\n",
      "585 True\n",
      "586 True\n",
      "587 True\n",
      "588 True\n",
      "589 True\n",
      "590 True\n",
      "591 True\n",
      "592 True\n",
      "593 True\n",
      "594 True\n",
      "595 True\n",
      "596 True\n",
      "597 True\n",
      "598 True\n",
      "599 True\n"
     ]
    }
   ],
   "source": [
    "# a = 100\n",
    "# b = 100\n",
    "\n",
    "# However, for small integer objects, CPython caches them\n",
    "# this means that a and b point to the same object\n",
    "# print(a is b)\n",
    "\n",
    "for i in range(500, 600):\n",
    "    print(i, i is i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4e1b9d3-a5ee-475a-b187-af0a3bb1f257",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "# CPython does the same for short strings\n",
    "str1 = \"MPCS\" * 100\n",
    "str2 = \"MPCS\" * 100\n",
    "print(id(str1), id(str2))\n",
    "str1 is str2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12713d9c-f6da-44f3-9b12-db663cd3e6da",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## copy & deepcopy\n",
    "\n",
    "If `y = x` does not make a copy, how can we get one?\n",
    "\n",
    "We've seen the `.copy()` method on a few of our types.  Which ones?\n",
    "\n",
    "We can also use the `copy` module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "c7e81136-78ee-4503-bc66-443dfcbfbccf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4384496640\n",
      "4384370048\n",
      "[1, 2, 3, 4] [1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "x = [1, 2, 3]\n",
    "y = x.copy()\n",
    "\n",
    "print(id(x))\n",
    "print(id(y))\n",
    "\n",
    "x.append(4)\n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "10cafa1b-b1fe-4954-b005-4f6f953f36b5",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x is y False\n",
      "x[0] is y[0] True\n",
      "x[1] is y[1] True\n",
      "[[1, 2, 5], [3, 4]] \n",
      " [[1, 2, 5], [3, 4]]\n"
     ]
    }
   ],
   "source": [
    "# shallow copy example (nested mutables are not copied)\n",
    "\n",
    "x = [[1, 2], [3, 4]]\n",
    "y = x.copy()  # or copy.copy(x)\n",
    "\n",
    "print(\"x is y\", x is y)\n",
    "print(\"x[0] is y[0]\", x[0] is y[0])\n",
    "print(\"x[1] is y[1]\", x[1] is y[1])\n",
    "\n",
    "# print(x, y)\n",
    "x[0].append(5)\n",
    "print(x, \"\\n\", y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "21855e2a-435c-4ecf-a224-586b55ad03b7",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x[0] is z[0] False\n"
     ]
    }
   ],
   "source": [
    "# deep copy (nested mutables are copied)\n",
    "import copy\n",
    "\n",
    "# copy.copy(obj) --> same as obj.copy()\n",
    "z = copy.deepcopy(x)\n",
    "print(\"x[0] is z[0]\", x[0] is z[0])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "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.10.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
