1625 lines
40 KiB
Plaintext
1625 lines
40 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8d96c164-300f-4b01-907c-d645b5338b42",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"# Object Oriented Programming\n",
|
|
"\n",
|
|
"\n",
|
|
"A programming paradigm that utilizes the concepts of \"objects\" which represent related bits of data & code.\n",
|
|
"\n",
|
|
"A common misconception is that a language needs classes to be object-oriented. While classes are the most common feature provided in OO-focused languages, one can write code in any language that fits this paradigm.\n",
|
|
"\n",
|
|
"In a hypothetical language that only had data structures and functions, we might write code like:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f1101c65-ba9b-4a87-a0cb-71cdc3c73726",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"#costume_is_scary(person_a)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "a4ae30b4-bd82-46ac-90bd-f6d885018f52",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"person_a = {\"name\": \"Andy\", \"costume\": \"Cowboy\", \"candy\": []}\n",
|
|
"person_b = {\"name\": \"Gil\", \"costume\": \"Robot\", \"candy\": []}\n",
|
|
"person_c = {\"name\": \"Lisa\", \"costume\": \"Ghost\", \"candy\": []}\n",
|
|
"\n",
|
|
"candy_bag = [\"Kit Kat\", \"Kit Kat\", \"Lollipop\", \"M&Ms\"]\n",
|
|
"\n",
|
|
"def costume_is_scary(person : dict) -> bool:\n",
|
|
" return person[\"costume\"] in (\"Ghost\", \"Wolfman\", \"Mummy\")\n",
|
|
"\n",
|
|
"def do_trick(person):\n",
|
|
" print(f\"{person['name']} did a trick\")\n",
|
|
"\n",
|
|
"def trick_or_treat(person):\n",
|
|
" success = give_candy(candy_bag, person)\n",
|
|
" # extra candy for scary costumes!\n",
|
|
" if costume_is_scary(person):\n",
|
|
" give_candy(candy_bag, person)\n",
|
|
" if not success:\n",
|
|
" do_trick(person)\n",
|
|
"\n",
|
|
"def give_candy(candy_bag, person):\n",
|
|
" if candy_bag:\n",
|
|
" candy = random.choice(candy_bag)\n",
|
|
" candy_bag.remove(candy)\n",
|
|
" person[\"candy\"].append(candy)\n",
|
|
" return True\n",
|
|
" else:\n",
|
|
" return False"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "35e5ef1d-5ac3-4771-8ec7-cfeb00ee2fa9",
|
|
"metadata": {},
|
|
"source": [
|
|
"A common pattern to see is a lot of functions that need a particular data structure as a parameter. Objects give us a way to connect that code, making it more clear what should be passed in, and reducing the chance of errors.\n",
|
|
"\n",
|
|
"The same code might be rewritten as:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "94ddd4b7-3229-4307-b097-c0d03f5c5a73",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Person:\n",
|
|
" def __init__(self, name, costume):\n",
|
|
" self.name = name\n",
|
|
" self.costume = costume\n",
|
|
" self.candy = []\n",
|
|
"\n",
|
|
" def is_scary(self):\n",
|
|
" return self.costume in (\"Ghost\", \"Wolfman\", \"Mummy\")\n",
|
|
" \n",
|
|
" def do_trick(self):\n",
|
|
" self.tricks = True\n",
|
|
" print(f\"{self.name} did a trick\")\n",
|
|
" \n",
|
|
" def accept_candy(self, candy):\n",
|
|
" self.candy.append(candy)\n",
|
|
" \n",
|
|
"class NoCandy(Exception):\n",
|
|
" pass\n",
|
|
"\n",
|
|
"class House:\n",
|
|
" def __init__(self, initial_candy):\n",
|
|
" self.candy = initial_candy\n",
|
|
" \n",
|
|
" def get_candy(self):\n",
|
|
" if not self.candy:\n",
|
|
" raise NoCandy(\"no more candy!\")\n",
|
|
" candy = random.choice(self.candy)\n",
|
|
" self.candy.remove(candy)\n",
|
|
" return candy\n",
|
|
" \n",
|
|
"\n",
|
|
"def trick_or_treat(person, house):\n",
|
|
" try:\n",
|
|
" candy = house.get_candy()\n",
|
|
" person.accept_candy(candy)\n",
|
|
" if person.is_scary():\n",
|
|
" person.accept_candy(house.get_candy())\n",
|
|
" except NoCandy:\n",
|
|
" do_trick(person, house)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "3b581e45-4da7-4c39-8dbd-bf55d5836441",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"p = Person(\"James\", \"Wolfman\")\n",
|
|
"p2 = Person(\"Fred\", \"Mummy\")\n",
|
|
"l1 = list()\n",
|
|
"l2 = list()\n",
|
|
"p.is_scary()\n",
|
|
"p.accept_candy(\"Chocolate\")\n",
|
|
"p.candy"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "50cfecf9-db6a-46a3-8441-4b4765e97324",
|
|
"metadata": {},
|
|
"source": [
|
|
"This code provides blueprints for what data & actions a \"person\" has. We also take our \"candy_bag\" list and turn it into a full-fledged object as well, since presumably we'd have multiple copies of it in our real-world application."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "78fb4860-13c9-4c90-951c-c9b19ff6f47d",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Terminology\n",
|
|
"\n",
|
|
"- **Object** - An encapsulation of data & related operations.\n",
|
|
"- **Class** - A blueprint for an object, providing methods that will act on instances of the data.\n",
|
|
"- **Instance** - An object created from a class \"blueprint\".\n",
|
|
"- **Method** - A function that is tied to a specific class.\n",
|
|
"- **Attribute** - Data that is tied to a specific instance.\n",
|
|
"- **Constructor** - A special method that creates & populates an instance of a class.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cd8d1021-89d5-4572-8fc8-7083f0414ce6",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"## Everything in Python is an Object\n",
|
|
"\n",
|
|
"`isinstance` is the preferred way to check if an item is of a particular type.\n",
|
|
"\n",
|
|
"It can return true for multiple types, we'll see why this is the case shortly."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "eb098ee4-813d-4ede-9fba-04caf9922a76",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"isinstance([1, 2, 3], list)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "8f904840-e5f1-4f44-a11a-dcb568914b98",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"isinstance([1, 2, 3], tuple)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "b7c087da-0213-41e4-a298-873d2e677ab9",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"isinstance([1, 2, 3], object)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "1ad4e49c-dbc2-406b-9148-6f708c01f4b9",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"s = set([1,2,3])\n",
|
|
"\n",
|
|
"# using constructors here for demo purposes, generally would use a literal (e.g. [], 0, \"\") for these\n",
|
|
"ll = list() \n",
|
|
"ll.append(str())\n",
|
|
"ll.append(int())\n",
|
|
"ll.append(float())\n",
|
|
"ll.append(s)\n",
|
|
"ll.append(print)\n",
|
|
"\n",
|
|
"print(ll)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "ea08451b-862a-446f-beb4-13bd8fcaa869",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"[isinstance(item, object) for item in ll]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e4c2a53e-9070-4446-b72b-d54f63fe8ead",
|
|
"metadata": {},
|
|
"source": [
|
|
"Keeping this in mind can help keep things straight when we delve deeper into making our own objects.\n",
|
|
"\n",
|
|
"Let's revisit a few things that we already know:\n",
|
|
"\n",
|
|
"- each `list` is independent of all others, when you create a new via `list()` (or `[]`) that is an **instance**\n",
|
|
"- calling things like `.append` operate on the instance they are called from. \n",
|
|
"- Some methods modify the underlying object (`.append`) while others just provide a return value like any other function. (What are some non-modifying methods?)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e870d8fe-786d-4499-86fc-4808a89981a3",
|
|
"metadata": {},
|
|
"source": [
|
|
"(Placeholder: MW class got here)\n",
|
|
"## Classes in Python"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "350fc7e7-ded5-4d4c-bab5-59de74d23764",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Instances, Classes, and Instantiation\n",
|
|
"\n",
|
|
"One way to think of classes are as blueprints for creating specific realizations.\n",
|
|
"\n",
|
|
"The blueprint can specify features that vary from car to car (color, transmission type, etc.). We can create multiple car **instances** with different values for a given attribute."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "1fce0f7d-7065-432d-8672-8fefd2b11f67",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Honda Civic 2019\n",
|
|
"Chevy Volt 2022\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"class Car:\n",
|
|
" # __init__ is a special method\n",
|
|
" # known as a double-underscore or dunder method\n",
|
|
" # in Python it represents our constructor\n",
|
|
"\n",
|
|
" def __init__(self, make, model, year=2000):\n",
|
|
" #print(type(self))\n",
|
|
" self.make = make\n",
|
|
" self.model = model\n",
|
|
" self.year = year\n",
|
|
" self.mileage = 0\n",
|
|
" self.hybrid = False\n",
|
|
" \n",
|
|
"# to actually create Cars, we need to call this constructor\n",
|
|
"car1 = Car(\"Honda\", \"Civic\", 2019)\n",
|
|
"car2 = Car(\"Chevy\", \"Volt\", 2022)\n",
|
|
"print(car1.make, car1.model, car1.year)\n",
|
|
"print(car2.make, car2.model, car2.year)\n",
|
|
"car3 = car2"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "8cb58e57-c4ba-4066-ab43-2c8469757918",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"True"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"car3 is car2"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "18729ce3-7250-4a72-833c-de48ca03ac93",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"car2.year += 1"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "b8612eda-9d93-4c17-a724-e4f93064da20",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"2023\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(car3.year)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "07f8b1f1-c2cc-4e93-98a4-0f5cbdee72f4",
|
|
"metadata": {},
|
|
"source": [
|
|
"This is known as *instantiation*, making an instance of the class.\n",
|
|
"\n",
|
|
"### `self` & methods\n",
|
|
"\n",
|
|
"The first parameter of methods is always `self`. \n",
|
|
"\n",
|
|
"This parameter is never passed directly, but is a local reference to the object the instance is being called upon.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "68e49035-d0cc-4f24-b19b-c84d568a1233",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"0"
|
|
]
|
|
},
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"class Car:\n",
|
|
" def __init__(self, make, model, year):\n",
|
|
" self.make = make\n",
|
|
" self.model = model\n",
|
|
" self.year = year\n",
|
|
" self.mileage = 0\n",
|
|
" self.hybrid = False\n",
|
|
" self.driver = None\n",
|
|
" \n",
|
|
" def print_report(self):\n",
|
|
" print(f\"{self.year} {self.make} {self.model} with {self.mileage} miles\")\n",
|
|
" \n",
|
|
" def drive(self, miles):\n",
|
|
" self.mileage += miles\n",
|
|
" \n",
|
|
"car1 = Car(\"Honda\", \"Civic\", 2019)\n",
|
|
"car2 = Car(\"Chevy\", \"Volt\", 2022)\n",
|
|
"car2.mileage"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "0e2162f2-7e5a-451b-96e9-b3fd4c5bcfaa",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"2019 Honda Civic with 0 miles\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"car1.print_report()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "dbb352b0-7cc4-476f-9d58-e7fff9c3ae35",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"1000\n",
|
|
"2022 Chevy Volt with 1000 miles\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"car2.drive(500)\n",
|
|
"print(car2.mileage)\n",
|
|
"car2.print_report()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "756e9562-79bc-4e38-9eb5-e5c0e8622124",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"car1.print_report()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "4afa851b-562e-4b81-a92a-017275f7d246",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"print(car1.mileage)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "fec57402-3e07-482a-b51d-0dda7e25e031",
|
|
"metadata": {},
|
|
"source": [
|
|
"Because of `self`, methods can know which instance they are operating upon."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8fec9ea6-1e09-42c8-808d-0eb5952fb8e2",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### How does this work?\n",
|
|
"\n",
|
|
"This is confusing at first glance, where does `self` come from? \n",
|
|
"\n",
|
|
"It is actually the \"parameter before the dot\".\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "0c0e0e3b-308d-411f-842d-e422d8dc7968",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# explicitly call Car.print_report and pass self\n",
|
|
"#car2.print_report()\n",
|
|
"Car.print_report(car2) \n",
|
|
"# this is not how we call class methods! (but it works)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "6f0599d6-df4f-413d-9c87-ac7ca479b35f",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[4, 4]"
|
|
]
|
|
},
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"ll = []\n",
|
|
"ll.append(4)\n",
|
|
"list.append(ll, 4) # list is class, ll is self here\n",
|
|
"ll"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "41a91dbb-868b-4cf9-92fc-a417da548e4f",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### What happens if `self` is omitted?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "184e8e69-0eb8-4068-bb3c-a2aec2433fec",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Mistake:\n",
|
|
" def __init__(self):\n",
|
|
" print(\"constructor!\")\n",
|
|
" \n",
|
|
" def method_no_self():\n",
|
|
" print(\"method!\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"id": "42fb46df-2abb-4387-9e49-5ddb27d0c23a",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"constructor!\n"
|
|
]
|
|
},
|
|
{
|
|
"ename": "TypeError",
|
|
"evalue": "Mistake.method_no_self() takes 0 positional arguments but 1 was given",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
|
|
"Cell \u001b[0;32mIn[23], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m m \u001b[38;5;241m=\u001b[39m Mistake()\n\u001b[0;32m----> 2\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmethod_no_self\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# rewritten as Mistake.method_no_self(m)\u001b[39;00m\n",
|
|
"\u001b[0;31mTypeError\u001b[0m: Mistake.method_no_self() takes 0 positional arguments but 1 was given"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"m = Mistake()\n",
|
|
"m.method_no_self()\n",
|
|
"# rewritten as Mistake.method_no_self(m)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d9c92e9f-65cd-4a7a-847a-6f5651552672",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Attributes\n",
|
|
"\n",
|
|
"- Created on assignment, like other variables.\n",
|
|
"- `self.name = value`\n",
|
|
"- All attributes are accessible from inside the class and outside:\n",
|
|
" - `self.name` from inside.\n",
|
|
" - `instance_name.name` from outside.\n",
|
|
" \n",
|
|
"**Best practice: create all attributes inside constructor!**\n",
|
|
"\n",
|
|
"Why?"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "31f1a926-7099-4740-9f86-769967fa41a9",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"my_car = Car(\"DMC\", \"DeLorean\", 1982)\n",
|
|
"my_car.driver_name = \"Marty\" # allowed, but to be avoided\n",
|
|
"my_car.whatever_i_want = [1, 2, 3]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "8c0afa4e-0219-4b45-a527-b63263a04d5d",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print(my_car.driver)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a2082903-236a-4b8b-b47f-a491ca3c71d6",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Exception to the rule: function objects\n",
|
|
"\n",
|
|
"Functions are objects, and can have attributes assigned to them as well.\n",
|
|
"\n",
|
|
"We sometimes do this since there's no opportunity to assign them before. (Because functions do not have constructors we can modify.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f0e0212d-363e-4bde-bfa4-3e717b800f5b",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def f():\n",
|
|
" print(f\"called f()\")\n",
|
|
" #f.call_count = 0 # NO\n",
|
|
"f.call_count = 0"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "b15355ff-359d-4fc8-b47a-04e4c0d4afd5",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"f.call_count += 1\n",
|
|
"f()\n",
|
|
"print(f.call_count)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "1cbc7aa8-19b6-43eb-91bb-69b889bd2a59",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# using a decorator to add call_count to any function\n",
|
|
"def counter(func):\n",
|
|
" #inner.call_count\n",
|
|
" def inner(*args, **kwargs):\n",
|
|
" inner.call_count += 1\n",
|
|
" print(f\"call count {inner.call_count}\")\n",
|
|
" return func(*args, **kwargs)\n",
|
|
" inner.call_count = 0\n",
|
|
" return inner"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "ad71f5ff-e97d-4559-8c37-c4b1af0c0ec2",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"@counter\n",
|
|
"def f():\n",
|
|
" print(\"called f()\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "4654848c-b793-4847-a18c-072da3188868",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"@counter\n",
|
|
"def g():\n",
|
|
" print(f\"called g()\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d2e7192b-bb8c-4b55-9a38-1f9d00e650c7",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"f()\n",
|
|
"f()\n",
|
|
"f()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "fa824ffd-8301-41e3-89f3-ef474dcc3025",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"g()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b9ca97f1-7d64-4d27-a685-fa17251acf3b",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Encapsulation\n",
|
|
"\n",
|
|
"A class should be responsible for modifications to its own state.\n",
|
|
"\n",
|
|
"Why might it be a bad idea to allow users to change attributes?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "68b9d90d-17ed-40c2-a3f2-736d7d6357ca",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# oops!\n",
|
|
"car2.mileage -= 100\n",
|
|
"car2.hybrid = \"no\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "28b09877-bd45-4d57-a671-9927cc02f1aa",
|
|
"metadata": {},
|
|
"source": [
|
|
"Furthermore, imagine we've noticed sometimes `year` is an `int` and other times a `str`. We could decide to remedy this in our constructor like:\n",
|
|
"\n",
|
|
"```python\n",
|
|
"class Car:\n",
|
|
" def __init__(self, make, model, year):\n",
|
|
" self.make = make\n",
|
|
" self.model = model\n",
|
|
" self.year = int(year)\n",
|
|
" self.mileage = 0\n",
|
|
" self.hybrid = False\n",
|
|
" \n",
|
|
" def drive(self, miles):\n",
|
|
" if miles < 0:\n",
|
|
" return # maybe an error instead? \n",
|
|
" self.mileage += miles\n",
|
|
"```\n",
|
|
"\n",
|
|
"We can also protect against trying to roll back the odometer by driving in reverse.\n",
|
|
"\n",
|
|
"If other code is assigning to internal variables, we need to make these checks/changes in dozens of places.\n",
|
|
"\n",
|
|
"**Encapsulation** therefore allows the implementation of a class interface to be changed with *minimal impact* upon users of the class.\n",
|
|
"\n",
|
|
"Good object-oriented design involves thinking through what **interface** you're providing to code making use of your objects.\n",
|
|
"\n",
|
|
"Python has changed how (e.g.) `dict` works internally several times over the decades. Imagine if each time they did so, methods like `.keys()` and `pop()` stopped working."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "638405f2-8e72-4450-95e6-973ffcbee224",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"### \"private\" in Python\n",
|
|
"\n",
|
|
"Some languages use access specifiers like \"private\", \"public\", \"protected\" to handle this. Python instead relies on convention.\n",
|
|
"\n",
|
|
"A name with a single underscore at the front is meant to be \"internal\" to the class, and should not be modified except from methods of that class.\n",
|
|
"\n",
|
|
"A name with a double underscore at the front is actually modified internally by Python to avoid people assigning to it accidentally.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"id": "41292841-0aa9-4bfd-9cfe-c12f860bbdd9",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"2022 Chevy Volt with 500 miles\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"class Car: \n",
|
|
" def __init__(self, make, model, year):\n",
|
|
" self._make = make \n",
|
|
" self._model = model \n",
|
|
" self._year = year\n",
|
|
" self.__mileage = 0\n",
|
|
" \n",
|
|
" def drive(self, miles):\n",
|
|
" if miles > 0:\n",
|
|
" self.__mileage += miles\n",
|
|
" else:\n",
|
|
" ...\n",
|
|
" \n",
|
|
" def print_report(self):\n",
|
|
" print(f\"{self._year} {self._make} {self._model} with {self.__mileage} miles\")\n",
|
|
" \n",
|
|
"car1 = Car(\"Honda\", \"Civic\", 2019)\n",
|
|
"car2 = Car(\"Chevy\", \"Volt\", 2022)\n",
|
|
"\n",
|
|
"car2.drive(500)\n",
|
|
"car2.print_report()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "3b3fcebf-571c-424b-88f2-dbb8fb029705",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"car2._year"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 28,
|
|
"id": "ef88e138-13c2-4174-b51f-03d1c3aad5aa",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"ename": "AttributeError",
|
|
"evalue": "'Car' object has no attribute '__mileage'",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
|
|
"Cell \u001b[0;32mIn[28], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m car2\u001b[38;5;241m.\u001b[39m__mileage \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m100\u001b[39m\n",
|
|
"\u001b[0;31mAttributeError\u001b[0m: 'Car' object has no attribute '__mileage'"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"car2.__Car_mileage -= 100"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "d09cbac0-fba3-420c-9760-1efe24a9ce48",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"['_Car__mileage',\n",
|
|
" '__class__',\n",
|
|
" '__delattr__',\n",
|
|
" '__dict__',\n",
|
|
" '__dir__',\n",
|
|
" '__doc__',\n",
|
|
" '__eq__',\n",
|
|
" '__format__',\n",
|
|
" '__ge__',\n",
|
|
" '__getattribute__',\n",
|
|
" '__gt__',\n",
|
|
" '__hash__',\n",
|
|
" '__init__',\n",
|
|
" '__init_subclass__',\n",
|
|
" '__le__',\n",
|
|
" '__lt__',\n",
|
|
" '__module__',\n",
|
|
" '__ne__',\n",
|
|
" '__new__',\n",
|
|
" '__reduce__',\n",
|
|
" '__reduce_ex__',\n",
|
|
" '__repr__',\n",
|
|
" '__setattr__',\n",
|
|
" '__sizeof__',\n",
|
|
" '__str__',\n",
|
|
" '__subclasshook__',\n",
|
|
" '__weakref__',\n",
|
|
" '_make',\n",
|
|
" '_model',\n",
|
|
" '_year',\n",
|
|
" 'drive',\n",
|
|
" 'print_report']"
|
|
]
|
|
},
|
|
"execution_count": 29,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"dir(car2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "cb2ba9e0-a5af-45bd-bc21-c25bc9414637",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"car2._make = \"???\"\n",
|
|
"print(car2._make) \n",
|
|
"# soft protection, can still access but \"at your own risk\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "4ea90f2f-6530-4732-b151-4fca1c431ab1",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print(car2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 33,
|
|
"id": "886b1b5a-d47a-4112-b37d-7117fd16e37a",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"<__main__.Car object at 0x103792d40>\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"carA = Car(\"Honda\", \"Civic\", 2019)\n",
|
|
"carB = Car(\"Honda\", \"Civic\", 2019)\n",
|
|
"carA == carB\n",
|
|
"\n",
|
|
"print(carA)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5320f144-0164-4f09-b191-7e5eae6c020c",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Dunder Methods\n",
|
|
"\n",
|
|
"Methods that begin and end with double-underscore are called special methods or dunder methods. These allow us to define classes that implement existing protocols.\n",
|
|
"\n",
|
|
"* `__repr__`\n",
|
|
"* `__str__`\n",
|
|
"* `__eq__`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 50,
|
|
"id": "9647e5ad-86a4-486f-bf7e-44668d25a472",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Car: \n",
|
|
" def __init__(self, make, model, year):\n",
|
|
" self._make = make \n",
|
|
" self._model = model \n",
|
|
" self._year = year\n",
|
|
" self.__mileage = 0\n",
|
|
"\n",
|
|
" def drive(self, miles):\n",
|
|
" if miles > 0:\n",
|
|
" self.__mileage += miles\n",
|
|
" else:\n",
|
|
" ...\n",
|
|
" \n",
|
|
" def __eq__(self, other):\n",
|
|
" # we can decide equality does/doesn't include mileage\n",
|
|
" return (self._make == other._make \n",
|
|
" and self._model == other._model \n",
|
|
" and self._year == other._year)\n",
|
|
" \n",
|
|
" def __repr__(self):\n",
|
|
" return f\"repr Car({self._make}, {self._model}, {self._year}, mileage={self.__mileage})\"\n",
|
|
" __str__ = __repr__\n",
|
|
" #def __str__(self):\n",
|
|
" # return f\"str {self._year} {self._make} {self._model} with {self.__mileage} miles\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "14e8fbb4-0453-425d-804e-ee2a4aa44976",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 51,
|
|
"id": "eb77364b-b30d-4a10-b0fb-355fa83e003d",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"truck = Car(\"Ford\", \"F-150\", 1985)\n",
|
|
"truck2 = Car(\"Ford\", \"F-150\", 1985)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 52,
|
|
"id": "d8302542-27c1-4b1b-9bce-59c6d7ce527c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"repr Car(Ford, F-150, 1985, mileage=0)"
|
|
]
|
|
},
|
|
"execution_count": 52,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"truck"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 53,
|
|
"id": "5c124af8-9d7d-4909-9124-1ba2bb170f16",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"repr Car(Ford, F-150, 1985, mileage=0)"
|
|
]
|
|
},
|
|
"execution_count": 53,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def f():\n",
|
|
" return truck\n",
|
|
"f()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 54,
|
|
"id": "e5b3f849-83b8-4625-b160-1141e39a1a99",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"repr Car(Ford, F-150, 1985, mileage=0)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(truck)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 48,
|
|
"id": "8f4919d6-bab6-49d4-9c73-dbf0790b118c",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'str 1985 Ford F-150 with 0 miles'"
|
|
]
|
|
},
|
|
"execution_count": 48,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"str(truck)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 37,
|
|
"id": "74bf6a50-b18d-443d-a0ef-e2f1b8004898",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"False"
|
|
]
|
|
},
|
|
"execution_count": 37,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"truck == car1 # eq"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 40,
|
|
"id": "01bdde7f-c4a8-4014-9f75-ff041370892d",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"True"
|
|
]
|
|
},
|
|
"execution_count": 40,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"truck.__eq__(truck2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"id": "b29e3c72-a392-4991-aeb5-fee9463495f2",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"True"
|
|
]
|
|
},
|
|
"execution_count": 38,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"truck == truck2 # eq"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 41,
|
|
"id": "26edc5e9-84ad-4e6f-9e9f-c7a546f7f7b7",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"False"
|
|
]
|
|
},
|
|
"execution_count": 41,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"truck is truck2"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d08d5dbf-7662-4534-8a3a-d44a79ee56f0",
|
|
"metadata": {},
|
|
"source": [
|
|
"### `str` vs `repr`\n",
|
|
"\n",
|
|
"`repr` is supposed to be a programmatic interpretation, used in debugging output. In jupyter/ipython if a function returns a value we see the repr by default.\n",
|
|
"\n",
|
|
"`str` is used when print is called, or an explicit conversion to string as shown above.\n",
|
|
"\n",
|
|
"If only `__repr__` is defined, then `str(obj)` will use `__repr__`, so if you don't have a need for them to differ, then define `__repr__`."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3f21be66-4b7a-4ef7-97e0-ff1c382993f6",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"## Protocols, Duck-Typing, and Polymorphism\n",
|
|
"\n",
|
|
"In a language like C++, functions can be created with one name but different argument lists.\n",
|
|
"\n",
|
|
"```c++\n",
|
|
"void foo(int x)\n",
|
|
"void foo(double x)\n",
|
|
"void foo(int x, double y)\n",
|
|
"```\n",
|
|
"\n",
|
|
"The compiler can decide which function to call at compile time based on the types given.\n",
|
|
"\n",
|
|
"This is called \"polymorphism\".\n",
|
|
"\n",
|
|
"We've seen one way to achieve similar results via variadic arguments.\n",
|
|
"\n",
|
|
"In Python, polymorphism stems from the idea **\"the meaning of an operation depends on the objects being operated on\"**.\n",
|
|
"\n",
|
|
"```python\n",
|
|
"1 + 5 # addition\n",
|
|
"\"1\" + \"5\" # string concatenation\n",
|
|
"[1,2,3] + [4,5] # list concatenation\n",
|
|
"```\n",
|
|
"\n",
|
|
"Remember, we mentioned that everything in Python is an `object` and `object`s have operations associated with them. \n",
|
|
"\n",
|
|
"```python\n",
|
|
"def times(x, y):\n",
|
|
" return x * y\n",
|
|
"```\n",
|
|
"\n",
|
|
"As long as our objects `x` and `y` support the `*` protocol, it is safe to call `times(x, y)`.\n",
|
|
"\n",
|
|
"### Duck Typing\n",
|
|
"\n",
|
|
"In Python, instead of forcing our arguments to be specific types, we use something known as \"Duck Typing.\" This comes from the expression:\n",
|
|
"\"If it looks like a duck, and it quacks like a duck, it might as well be a duck.\"\n",
|
|
"\n",
|
|
"## Protocols\n",
|
|
"\n",
|
|
"Another way of thinking about this is that objects of a given type follow a certain protocol.\n",
|
|
"\n",
|
|
"- \"addable\"\n",
|
|
"- \"comparable\"\n",
|
|
"- \"iterable\"\n",
|
|
"- \"callable\"\n",
|
|
"\n",
|
|
"These are typically implemented via dunder methods. To be \"addable\" an object needs a `__add__` method at minimum. To be comparable it needs `__eq__` and `__lt__` or `__gt__` at least.\n",
|
|
"\n",
|
|
"Let's look at iterable now through this new lens:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 56,
|
|
"id": "a161eedc-520a-4827-a0ba-701f1cba3376",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"1\n",
|
|
"4\n",
|
|
"9\n",
|
|
"16\n",
|
|
"25\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"l = [1, 2, 3, 4, 5]\n",
|
|
"g = (x**2 for x in l)\n",
|
|
"r = range(8)\n",
|
|
"\n",
|
|
"# iterable: we can use it in a for loop\n",
|
|
"for x in g: # or g, or r\n",
|
|
" print(x)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "9f73f6a7-b97d-457d-92bc-f96ee9f45246",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"g"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2caf44fa-74e1-4e42-95d3-f0a070d34dc7",
|
|
"metadata": {},
|
|
"source": [
|
|
"What actually happens here?\n",
|
|
"\n",
|
|
"The iteration protocol has a few requirements:\n",
|
|
"\n",
|
|
"- When an iterable is passed to an iteration context (for loop, comprehension, `map`, etc.) The context will call `iter(iterable)` to obtain the object's iterator. \n",
|
|
"- `iter(obj)` calls `obj.__iter__`\n",
|
|
"- Iterator objects return values one at a time, we can call `next(iterator)` to obtain the next value. \n",
|
|
"`next(it)` calls `it.__next__`\n",
|
|
"- `StopIteration` is raised when there are no more values."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 58,
|
|
"id": "2616192f-8adc-45a8-a988-3558aebe1eb3",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<list_iterator at 0x1042a85e0>"
|
|
]
|
|
},
|
|
"execution_count": 58,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"iter([1, 2, 3])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 59,
|
|
"id": "a71a043a-1c1b-4c88-b489-b5e47b9f2d4f",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"<list_iterator object at 0x1042ab6d0>\n",
|
|
"<generator object <genexpr> at 0x104622a40>\n",
|
|
"<range_iterator object at 0x1042abb70>\n",
|
|
"<list_iterator object at 0x1042a9c00>\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"l = [1, 2, 3]\n",
|
|
"g = (x**2 for x in l)\n",
|
|
"r = range(8)\n",
|
|
"\n",
|
|
"li = iter(l)\n",
|
|
"gi = iter(g)\n",
|
|
"ri = iter(r)\n",
|
|
"li2 = iter(l)\n",
|
|
"\n",
|
|
"print(li)\n",
|
|
"print(gi)\n",
|
|
"print(ri)\n",
|
|
"print(li2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 62,
|
|
"id": "6aec1be0-9094-450c-95f7-91ebec9c3c76",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"3"
|
|
]
|
|
},
|
|
"execution_count": 62,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"next(li)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 63,
|
|
"id": "032cfb1f-25f6-4359-8190-53129ec1b67b",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"1"
|
|
]
|
|
},
|
|
"execution_count": 63,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"next(li2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "dbb7148c-af4a-434b-9863-1ff7083fab05",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"#print(next(li))\n",
|
|
"#print(next(gi))\n",
|
|
"print(next(ri))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 64,
|
|
"id": "4260dadd-9476-4103-bf7f-496812904a1e",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"ename": "StopIteration",
|
|
"evalue": "",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)",
|
|
"Cell \u001b[0;32mIn[64], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mli\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28mnext\u001b[39m(gi))\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28mnext\u001b[39m(ri))\n",
|
|
"\u001b[0;31mStopIteration\u001b[0m: "
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(next(li))\n",
|
|
"print(next(gi))\n",
|
|
"print(next(ri))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "599aa900-9cc4-4579-b94a-8c0474e4ef06",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"### Discussion\n",
|
|
"\n",
|
|
"- What else is iterable?\n",
|
|
"- What are other protocols we've seen?\n",
|
|
"- Do all iterables eventually raise `StopIteration`?"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 65,
|
|
"id": "6b4cb301-3152-4cd2-bba7-6f0cdd258afe",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"1\n",
|
|
"2\n",
|
|
"3\n",
|
|
"4\n"
|
|
]
|
|
},
|
|
{
|
|
"ename": "StopIteration",
|
|
"evalue": "",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)",
|
|
"Cell \u001b[0;32mIn[65], line 4\u001b[0m\n\u001b[1;32m 2\u001b[0m iterator \u001b[38;5;241m=\u001b[39m \u001b[38;5;28miter\u001b[39m(iterable)\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43miterator\u001b[49m\u001b[43m)\u001b[49m)\n",
|
|
"\u001b[0;31mStopIteration\u001b[0m: "
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"iterable = [1, 2, 3, 4] # iterable\n",
|
|
"iterator = iter(iterable)\n",
|
|
"while True:\n",
|
|
" print(next(iterator))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f7e925d3-f7ad-4ef1-97bd-8300dbb346e7",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"f(x[0] + y[\"test\"])\n",
|
|
"\n",
|
|
"f.__call__(x.__getitem__(0).__add__(y.__getitem__(\"test\")))"
|
|
]
|
|
}
|
|
],
|
|
"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
|
|
}
|