Skip to content

Instantly share code, notes, and snippets.

@refraction-ray
Last active February 24, 2019 02:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save refraction-ray/f001c6297cf6ba824d906b59a8bdcf8d to your computer and use it in GitHub Desktop.
Save refraction-ray/f001c6297cf6ba824d906b59a8bdcf8d to your computer and use it in GitHub Desktop.
A minimal tutorial on functools module in python
Display the source blob
Display the rendered blob
Raw
{
"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