Skip to content

Instantly share code, notes, and snippets.

@shane5ul
Created September 29, 2023 12:48
Show Gist options
  • Save shane5ul/c570c26e5c60eb00199ebb6d757e0779 to your computer and use it in GitHub Desktop.
Save shane5ul/c570c26e5c60eb00199ebb6d757e0779 to your computer and use it in GitHub Desktop.
A jupyter notebook demonstrating python's unpacking operators * and **.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Python Unpacking Operators\n",
"\n",
"Iterables are objects like lists, tuples, and dictionaries\n",
"\n",
"The operators `*` (lists and tuples) and `**` (dictionaries) are flexible ways to unpack the iterables that they work on. Since python 3.5, they have become more flexible and powerful.\n",
"\n",
"## Unpacking iterables\n",
"\n",
"They can be used to flatten iterables. You can use this to flatten lists r nerge dictionaries.\n",
"\n",
"The *[2, 3] open up the list as 2, 3."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[1, *[2, 3]]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'chocolate': 2, 'strawberry': 2, 'vanilla': 3}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dict1 = {'vanilla':3, 'chocolate':2}\n",
"{**dict1, 'strawberry':2}"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'almond': 5, 'chocolate': 2, 'pista': 4, 'vanilla': 3}"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dict2 = {'pista':4, 'almond':5}\n",
"{**dict1, **dict2}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"They can even be used with generators."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[1, 4, *(x**2 for x in range(3,6))]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This can be handy in parsing an iterable with a variable number of elements."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 3\n",
"1 [2, 3, 4] 5\n"
]
}
],
"source": [
"first, middle, last = [1, 2, 3]\n",
"print(first, middle, last)\n",
"\n",
"first, *middle, last = [1, 2, 3, 4, 5]\n",
"print(first, middle, last)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you only care about the first and last elements. While most examples use lists, these comprehensions also work on tuples."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 5\n"
]
}
],
"source": [
"first, *_, last = (1, 2, 3, 4, 5)\n",
"print(first, last)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since python 3.5, you can use the unpacking operator in [new and interesting](https://stackoverflow.com/questions/35636785/star-operator-on-left-vs-right-side-of-an-assignment-statement) ways\n",
"\n",
"`*elements, = iterable # creates a list; note ,`\n",
"\n",
"`*` on the LHS of `=` means \"catch everything that isn't assigned to a name and assign it to the starred expression\"\n",
"\n",
"`elements = *iterable # creates a tuple`\n",
"\n",
"Here we don't have the `*`` working in a \"catch everything\" way as much as we have it working as it usually does in function calls. It expands the contents of the iterable it is attached to. So, the statement is equivalent to (say)\n",
"\n",
"`elements = 1, 2, 3, 4,`\n",
"\n",
"which is another way for a tuple to be initialized. To put it in a list instead, use:\n",
"\n",
"\n",
"`elements = [*iterable] # creates a tuple and puts in a list`\n",
"\n",
"## Functions\n",
"\n",
"Generally unpacking is used to define functions with arbitrary number of inputs. But unpacking can also be used to call a function with a fixed number of inputs. Consider the following example."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"12\n"
]
}
],
"source": [
"def product(n1, n2):\n",
" return n1 * n2\n",
" \n",
"numbers = [12, 1]\n",
"print(product(*numbers))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However, the more common use following familiar structure.\n",
"\n",
"* It does not **have** to be called `args` and `kwargs`\n",
"* `args` is stored as a tuple, and `kwargs` as a dict"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"def add_num(*args, **kwargs):\n",
" res = 0.\n",
"\n",
"\n",
" if len(kwargs) > 0:\n",
" if kwargs['verbose']:\n",
" print(\"type args\", type(args))\n",
" print(\"type kwargs\", type(kwargs))\n",
"\n",
" print(\"args = \", args)\n",
" print(\"kwargs = \", kwargs)\n",
" \n",
" for x in args:\n",
" res += x\n",
" return res"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6.0"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = 1; b = 2; c = 3\n",
"add_num(a, b, c, verbose=False)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"type args <class 'tuple'>\n",
"type kwargs <class 'dict'>\n",
"args = (1, 2)\n",
"kwargs = {'verbose': True}\n"
]
},
{
"data": {
"text/plain": [
"3.0"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add_num(a, b, verbose=True)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.0"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add_num()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zip\n",
"\n",
"The [zip()](https://www.programiz.com/python-programming/methods/built-in/zip) function takes iterables (can be zero or more), aggregates them in a tuple, and returns it.\n",
"\n",
"The zip() function returns an iterator of tuples based on the iterable objects.\n",
"\n",
"The unpacking operator can be used to unzip."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<zip object at 0x7fca50625488> [('Java', 14), ('Python', 3), ('JavaScript', 6)]\n"
]
}
],
"source": [
"languages = ['Java', 'Python', 'JavaScript']\n",
"versions = [14, 3, 6]\n",
"\n",
"result = zip(languages, versions)\n",
"result_list = list(result)\n",
"print(result, result_list)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('Java', 'Python', 'JavaScript')\n",
"(14, 3, 6)\n"
]
}
],
"source": [
"x, y = zip(*result_list)\n",
"print(x)\n",
"print(y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some use cases for zip. "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Java 14\n",
"Python 3\n",
"JavaScript 6\n"
]
}
],
"source": [
"# Parallel iteration in a loop\n",
"for x, y in zip(languages, versions):\n",
" print(x, y)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'id': '13', 'location': 'redmond', 'name': 'bill'}"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Building dictionaries from corresponding iterables\n",
"fields = [\"id\", \"name\", \"location\"]\n",
"values = [\"13\", \"bill\", \"redmond\"]\n",
"dict(zip(fields, values))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"((0, 1, 2, 3, 4), (1, 2, 3, 4, 5))"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Column to row view:\n",
"five_by_two = ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5))\n",
"two_by_five = tuple(zip(*five_by_two))\n",
"two_by_five"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(1, 2)\n"
]
},
{
"data": {
"text/plain": [
"3.0"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add_num(a, b)"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"()\n"
]
},
{
"data": {
"text/plain": [
"0.0"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add_num()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment