Last active
February 24, 2019 02:27
-
-
Save refraction-ray/f001c6297cf6ba824d906b59a8bdcf8d to your computer and use it in GitHub Desktop.
A minimal tutorial on functools module in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# functools usage\n", | |
"**Reference:** [doc](https://docs.python.org/3.6/library/functools.html)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import functools" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"heading_collapsed": true | |
}, | |
"source": [ | |
"## lru_cache decorator\n", | |
"This can cache the result of a function, such that further call can be returned directly.\n", | |
"Internally, it use a dict to store corresponding results, so all the arguments must be hashable.\n", | |
"Such feature is especially useful in dynamicall programming context." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [], | |
"source": [ | |
"@functools.lru_cache(maxsize=200) # maxsize can be set to None, where infinite results are cached, caution!\n", | |
"def add1(n):\n", | |
" print(\"caculating...\")\n", | |
" return n+1" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"caculating...\n", | |
"caculating...\n", | |
"caculating...\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"(2, 3, 2, 4, 4)" | |
] | |
}, | |
"execution_count": 4, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add1(1), add1(2), add1(1), add1(3), add1(3)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"CacheInfo(hits=2, misses=3, maxsize=200, currsize=3)" | |
] | |
}, | |
"execution_count": 5, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add1.cache_info() # you can even check the hit rate!!" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"caculating...\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"2" | |
] | |
}, | |
"execution_count": 6, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add1.cache_clear() # clean the cache\n", | |
"add1(1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"heading_collapsed": true | |
}, | |
"source": [ | |
"## partial\n", | |
"make partial functions" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def add(x,y):\n", | |
" print(f\"{x}+{y}\")\n", | |
" return x+y\n", | |
"add1 = functools.partial(add,2)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2+1\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"3" | |
] | |
}, | |
"execution_count": 15, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add1(1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"heading_collapsed": true | |
}, | |
"source": [ | |
"## reduce" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"hidden": true | |
}, | |
"source": [ | |
"The usage of reduce is evident, and no further explanation here" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"1+2\n", | |
"3+3\n", | |
"6+4\n", | |
"10+5\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"15" | |
] | |
}, | |
"execution_count": 16, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"functools.reduce(add, [1,2,3,4,5])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": { | |
"hidden": true | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"10+1\n", | |
"11+2\n", | |
"13+3\n", | |
"16+4\n", | |
"20+5\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"25" | |
] | |
}, | |
"execution_count": 18, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"functools.reduce(add, [1,2,3,4,5], 10) # the third argument is the start point of reduce " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## singledispatch" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"function overload based on the type of the first argument" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 41, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"<function __main__.<lambda>>" | |
] | |
}, | |
"execution_count": 41, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"@functools.singledispatch\n", | |
"def show(arg, n): # the default function\n", | |
" print(\"default\")\n", | |
"\n", | |
"@show.register(int)\n", | |
"def show_int(arg, n): # note the function name here is not the same as show\n", | |
" return arg+n\n", | |
"\n", | |
"@show.register(list)\n", | |
"def show_list(arg, n):\n", | |
" return [a+n for a in arg]\n", | |
"\n", | |
"show.register(float, lambda arg, n: arg+n) # the overload function can also register directly" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 42, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"(5, 3.2, [1, 2, 3])" | |
] | |
}, | |
"execution_count": 42, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"show(2,3), show(2.2,1), show([1,2,3], 0)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 43, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"default\n" | |
] | |
} | |
], | |
"source": [ | |
"show((2,3),1)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 44, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"(<function __main__.show>, <function __main__.show_list>)" | |
] | |
}, | |
"execution_count": 44, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"show.dispatch(set), show.dispatch(list)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## wraps" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Maybe the most used one in this module, used in definition of new decorators, \n", | |
"such that relevant attrs of wrapper function keep the original form" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 45, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# define a decorator without using wraps\n", | |
"def log(f):\n", | |
" def wrapper(*args, **kws):\n", | |
" print(f\"call {f.__name__}\")\n", | |
" return f(*args, **kws)\n", | |
" return wrapper" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 46, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"@log\n", | |
"def add(a, b):\n", | |
" return a+b" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 47, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"call add\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"3" | |
] | |
}, | |
"execution_count": 47, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add(1,2) ## the log decorator works" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 48, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"'wrapper'" | |
] | |
}, | |
"execution_count": 48, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add.__name__ ## but the function name is not as expected, we hope the name is still add\n", | |
"# which make the decorator transparency" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 55, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# try define the same decorator with wrap tool\n", | |
"def log(f):\n", | |
" @functools.wraps(f)\n", | |
" def wrapper(*args, **kws):\n", | |
" print(f\"call {f.__name__}\")\n", | |
" return f(*args, **kws)\n", | |
" return wrapper" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 56, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"call add\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"3" | |
] | |
}, | |
"execution_count": 56, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"@log\n", | |
"def add(a, b):\n", | |
" return a+b\n", | |
"add(1,2)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 57, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"'add'" | |
] | |
}, | |
"execution_count": 57, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add.__name__ ## now the relevant attrs of add is correct again" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"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.6.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment