637 lines
15 KiB
Plaintext
637 lines
15 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3e9aa8bc-bdc1-4f86-a44b-c68b8072c4ef",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Decorators\n",
|
|
"\n",
|
|
"A common pattern in functional programs, are functions that are built to \"wrap\" other functions.\n",
|
|
"\n",
|
|
"Functions are in fact mutable."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "d8cecafd-c460-446b-8f8f-7a35a9f8c6e2",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def print_before_and_after(func):\n",
|
|
" def newfunc(*args, **kwargs):\n",
|
|
" print(\"BEFORE\", func)\n",
|
|
" func(*args, **kwargs)\n",
|
|
" print(\"AFTER\", func)\n",
|
|
" return newfunc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "b3dd55e9-33cb-41c9-840a-7fd2f137f2f0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "f327b88e-d034-4bd3-9e63-9196ccbb3d84",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def inner(a, b, c):\n",
|
|
" print(\"inner function\", a, b, c)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "359d03fc-1d69-4574-a06c-45b55905cecb",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"BEFORE <function inner at 0x103591240>\n",
|
|
"inner function 1 2 3\n",
|
|
"AFTER <function inner at 0x103591240>\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"wrapped_inner = print_before_and_after(inner)\n",
|
|
"\n",
|
|
"wrapped_inner(1, 2, 3)\n",
|
|
"#print(x)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "916c438d-f0f3-4b78-9846-f9bf2aab7dcc",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"BEFORE <function inner at 0x103591240>\n",
|
|
"inner function 1 2 3\n",
|
|
"AFTER <function inner at 0x103591240>\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# often we want to just replace the function altogether\n",
|
|
"# with the modified version\n",
|
|
"inner = print_before_and_after(inner)\n",
|
|
"inner(1, 2, 3)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "508bbf73-1f9f-41dd-9665-549472056211",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# another way of writing this is to use decorator syntax\n",
|
|
"@print_before_and_after\n",
|
|
"@print_before_and_after\n",
|
|
"def add_nums(a, b, c):\n",
|
|
" print(f\"{a} + {b} + {c} =\", a + b + c)\n",
|
|
"# add_nums = print_before_and_after(add_nums)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "86ee63a9-ac5d-44f5-a06a-a89527358141",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"BEFORE <function print_before_and_after.<locals>.newfunc at 0x103591ea0>\n",
|
|
"BEFORE <function add_nums at 0x1035923b0>\n",
|
|
"1 + 2 + 3 = 6\n",
|
|
"AFTER <function add_nums at 0x1035923b0>\n",
|
|
"AFTER <function print_before_and_after.<locals>.newfunc at 0x103591ea0>\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"add_nums(1, 2, 3)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "1e9d6428-8f80-4707-b508-c15878459d1f",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def cache(func):\n",
|
|
" inner_cache = {}\n",
|
|
" \n",
|
|
" def newfunc(*args, **kwargs):\n",
|
|
" if args not in inner_cache:\n",
|
|
" # will not work correctly if there are kwargs\n",
|
|
" inner_cache[args] = func(*args, **kwargs)\n",
|
|
" return inner_cache[args]\n",
|
|
" \n",
|
|
" return newfunc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "c50faf15-f5dc-4a1b-a114-c606a0edd0be",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"@cache\n",
|
|
"def expensive_calculation(a, b, *, c=0):\n",
|
|
" print(f\"doing expensive calculation on {a} {b}...\")\n",
|
|
" return a ** b\n",
|
|
"#expensive_calculation = cache(expensive_calculation)\n",
|
|
"\n",
|
|
"@cache\n",
|
|
"def cheap_calculation(a, b):\n",
|
|
" print(f\"doing cheap calculation on {a} {b}...\")\n",
|
|
" return a + b"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "dadaf583-82fd-4273-870e-7d68ec67c537",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"expensive_calculation(4, 10)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "6caa9b63-bc83-44c7-b416-1b5f9c708469",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"expensive_calculation(4, 10)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "c6e9a399-9c52-4b38-8fe0-427e0843804a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"cheap_calculation(4, 10)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "8ed9065a-4fb1-4486-ab02-b858d031eb93",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"expensive_calculation(5, 6)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "975f7f9e-74c4-4dcf-b396-ad85556e930a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"cheap_calculation(4, 10)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d3d8c310-4fa6-4b5e-b6e7-e0512adfe8c0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"expensive_calculation(5, 6)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"id": "b6da10be-562c-4968-86ed-c473db63eccf",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"ename": "TypeError",
|
|
"evalue": "repeatN() missing 1 required positional argument: 'n'",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
|
|
"Cell \u001b[0;32mIn[31], line 8\u001b[0m\n\u001b[1;32m 4\u001b[0m func(a, b, c)\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m newfunc\n\u001b[1;32m 7\u001b[0m \u001b[38;5;129;43m@repeatN\u001b[39;49m\n\u001b[0;32m----> 8\u001b[0m \u001b[38;5;28;43;01mdef\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;21;43mprint_sum\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 9\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43msum\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n",
|
|
"\u001b[0;31mTypeError\u001b[0m: repeatN() missing 1 required positional argument: 'n'"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def repeat5(func): # the decorator \n",
|
|
" def newfunc(a, b, c): # the inner function\n",
|
|
" return func(a, b, c, c, b, a)\n",
|
|
" return newfunc\n",
|
|
"\n",
|
|
"@repeatN\n",
|
|
"def print_sum(*args):\n",
|
|
" print(sum(args))\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "7f43e771-92d9-48d3-96ed-b95b1c94e942",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"6\n",
|
|
"6\n",
|
|
"6\n",
|
|
"6\n",
|
|
"6\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print_sum(1, 2, 3)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "4327b95d-b9ab-49e3-a530-b34e0688cff6",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n",
|
|
"sdrawkcab\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# to make a decorator that takes additional arguments\n",
|
|
"# you need to write a decorator factory function that returns decorators\n",
|
|
"\n",
|
|
"total = 123\n",
|
|
"\n",
|
|
"def repeat(start, end): # factory: takes integer, returns decorator\n",
|
|
" def repeat_decorator(func): # decorator: takes function, returns function\n",
|
|
" def newfunc(*args, **kwargs): # inner function: takes ?, returns ?\n",
|
|
" for i in range(total):\n",
|
|
" func(*args, **kwargs)\n",
|
|
" return newfunc\n",
|
|
" return repeat_decorator\n",
|
|
"\n",
|
|
"@repeat(10)\n",
|
|
"def print_backwards(s):\n",
|
|
" print(s[::-1])\n",
|
|
"\n",
|
|
"print_backwards(\"backwards\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "8b4219ac-7326-47e1-b3ad-68941349db1d",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"repeat_10 = repeat(10)\n",
|
|
"print(repeat_10)\n",
|
|
"print_backwards = repeat_10(print_backwards)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "a1d27a5c-ce00-4023-8efd-1900b6f876e5",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Example: writing our own `partial`\n",
|
|
"# https://docs.python.org/3/library/functools.html#functools.partial"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"id": "fb9e74b4-a0a2-4463-add7-45b04430edcd",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import functools\n",
|
|
"print_hello_names = functools.partial(print, \"Hello\", sep=\", \")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 33,
|
|
"id": "e64ec996-4085-4c03-a771-6c1073b04990",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Hello, Scott, Paul, Lauren\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names(\"Scott\", \"Paul\", \"Lauren\")\n",
|
|
"# same as print(\"Hello\", \"Scott\", \"Paul\", \"Lauren\", sep=\", \")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "eb0d1ad3-e636-4430-9139-265740adc8a1",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"('Hello',)"
|
|
]
|
|
},
|
|
"execution_count": 17,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names.args"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "9e67124b-9014-49d9-af86-54b8a617521f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'sep': ', '}"
|
|
]
|
|
},
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names.keywords"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "f4939684-6c92-4c07-8d88-f7c4349d0f02",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<function print>"
|
|
]
|
|
},
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names.func"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 36,
|
|
"id": "1ed27205-7691-46ee-bdc6-f75dae7d53da",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# since functions are objects, we can attach arbitrary values to them\n",
|
|
"def wrapper(func):\n",
|
|
" def newfunc(*args, **kwargs):\n",
|
|
" return func(*args, **kwargs)\n",
|
|
" # we can do whatever we like after defining newfunc, but before returning it\n",
|
|
" newfunc.xyz = \"hello\"*2\n",
|
|
" return newfunc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 35,
|
|
"id": "8e02bb23-7278-4799-85d6-9004af65565b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"True"
|
|
]
|
|
},
|
|
"execution_count": 35,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# property is assigned to all wrapped functions\n",
|
|
"@wrapper\n",
|
|
"def our_function():\n",
|
|
" print(\"inside our function\")\n",
|
|
"\n",
|
|
"our_function.xyz"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 40,
|
|
"id": "93b1ad42-b4a2-4947-a4bb-19c818968e2c",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def our_partial(func, /, *args, **keywords):\n",
|
|
" def newfunc(*fargs, **fkeywords):\n",
|
|
" newkeywords = {**keywords, **fkeywords}\n",
|
|
" return func(*args, *fargs, **newkeywords)\n",
|
|
" # assign these properties from within the closure\n",
|
|
" newfunc.func = func\n",
|
|
" newfunc.args = args\n",
|
|
" newfunc.keywords = keywords\n",
|
|
" return newfunc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 39,
|
|
"id": "092b1a1d-4300-4895-aa06-4d874bc61159",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Scott, Paul, Lauren, Hello!"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names2 = our_partial(print, \"Hello\", sep=\", \")\n",
|
|
"print_hello_names2(\"Scott\", \"Paul\", \"Lauren\", end=\"!\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 37,
|
|
"id": "a0bd355b-63d5-41e2-8238-2e3dff22cb45",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Hello?Scott?Paul?Lauren!"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"#print_hello_names2 = our_partial(print, \"Hello\", sep=\", \")\n",
|
|
"print_hello_names2(\"Scott\", \"Paul\", \"Lauren\", end=\"!\", sep=\"?\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "c48a5dfb-0444-4f5e-a4bc-39f0770ff5a3",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"('Hello',)"
|
|
]
|
|
},
|
|
"execution_count": 25,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names2.args"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"id": "f5d68812-918b-42dd-b23f-925a692668aa",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'sep': ', '}"
|
|
]
|
|
},
|
|
"execution_count": 26,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names2.keywords"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 27,
|
|
"id": "6b41b480-43e1-49b2-ade7-44d8aa484a09",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<function print>"
|
|
]
|
|
},
|
|
"execution_count": 27,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"print_hello_names2.func"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "ca9e90da-65e6-4e92-91ae-a97340527b38",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"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
|
|
}
|