Skip to content

Instantly share code, notes, and snippets.

@BroaderImpact
Created September 25, 2022 22:06
Show Gist options
  • Select an option

  • Save BroaderImpact/35fbf11b95d60e4b5f70aea100458a75 to your computer and use it in GitHub Desktop.

Select an option

Save BroaderImpact/35fbf11b95d60e4b5f70aea100458a75 to your computer and use it in GitHub Desktop.
Python Data Structures and Algorithms
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 1.1. Unpacking a Sequence into Separate Variables\n",
"## Problem\n",
"You have an N-element tuple or sequence that you would like to unpack into a collection\n",
"of N variables."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4\n",
"5\n"
]
}
],
"source": [
"p = (4, 5) # given p, a two-element tuple\n",
"x, y = p # assigns x and y to the two elements of p\n",
"print(x) # should print first element of p\n",
"print(y) # should print second element of p"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution\n",
"Any sequence (or iterable) can be unpacked into variables using a simple assignment operation. The only requirement is that the number of variables and structure match the sequence."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2012\n",
"ACME\n"
]
}
],
"source": [
"data = [ 'ACME', 50, 91.1, (2012, 12, 21) ] # given data, a four-element tuple\n",
"company, employees, rating, (year, month, day) = data # assigning the elements of data to individual variables\n",
"print(year)\n",
"print(company)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(2012, 12, 21)\n",
"12\n",
"50\n"
]
}
],
"source": [
"data = [ 'ACME', 50, 91.1, (2012, 12, 21) ] # given data, a four-element tuple\n",
"name, shares, price, date = data # reassigning with date as a single element\n",
"print(date)\n",
"print(month)\n",
"print(shares)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "not enough values to unpack (expected 3, got 2)",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-4-16f8f27ebf6e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 3, got 2)"
]
}
],
"source": [
"p = (4, 5) # given p, a two-element list, let's see what happens when we try to unpack into three variables\n",
"x, y, z = p # should give ValueError: not enough values to unpack (expected 3, got 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Discussion\n",
"Unpacking actually works with any object that happens to be iterable, not just tuples or\n",
"lists. This includes strings, files, iterators, and generators. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"H\n",
"e\n",
"o\n"
]
}
],
"source": [
"s = 'Hello' # Given a string, s\n",
"a, b, c, d, e = s # unpacking string s into five different elements\n",
"print(a)\n",
"print(b)\n",
"print(e)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 1.2. Unpacking Elements from Iterables of Arbitrary Length\n",
"## Problem\n",
"You need to unpack N elements from an iterable, but the iterable may be longer than N elements, causing a “too many values to unpack” exception."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution\n",
"Python “star expressions” can be used to address this problem. For example, suppose you run a course and decide at the end of the semester that you’re going to drop the first and last homework grades, and only average the rest of them. If there are only four assignments, maybe you simply unpack all four, but what if there are 24? A star expression makes it easy:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def drop_first_last(grades): # Defining function to drop first and last grades and return middle average\n",
" first, *middle, last = grades # Here, *middle may refer to multiple items to unpack\n",
" return avg(middle) # Here, you're averaging multiple assignments that are in the middle"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dave\n",
"dave@example.com\n",
"['773-555-1212', '847-555-1212']\n"
]
}
],
"source": [
"user_record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212') # Given user_record, which has multiple phone numbers\n",
"name, email, *phone_numbers = user_record # Star expression used here to denote multiple phone numbers\n",
"print(name)\n",
"print(email)\n",
"print(phone_numbers)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Pseudocode Example\n",
"\n",
"```\n",
"*trailing_qtrs, current_qtr = sales_record # Given sales_record, defined by all quarters prior to the current quarter\n",
"trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs) # Define new value, trailing average, only applicable to previous quarters\n",
"return avg_comparison(trailing_avg, current_qtr) # Returns comparison of trailing average and current quarter\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[10, 8, 7, 1, 9, 5, 10]\n",
"3\n"
]
}
],
"source": [
"*trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]\n",
"print(trailing)\n",
"print(current)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Discussion\n",
"Extended iterable unpacking is tailor-made for unpacking iterables of unknown or arbitrary length. Oftentimes, these iterables have some known component or pattern in their construction (e.g. “everything after element 1 is a phone number”), and star unpacking lets the developer leverage those patterns easily instead of performing acrobatics to get at the relevant elements in the iterable. It is worth noting that the star syntax can be especially useful when iterating over a sequence of tuples of varying length. "
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"foo 1 2\n",
"bar hello\n",
"foo 3 4\n"
]
}
],
"source": [
"records = [ # Given records, a sequence of tuples of varying length\n",
" ('foo', 1, 2),\n",
" ('bar', 'hello'), # The bar tuple is l=2 instead of 3\n",
" ('foo', 3, 4),\n",
"]\n",
"def do_foo(x, y): # Defining do_foo function as a means of printing foo and two arguments\n",
" print('foo', x, y)\n",
"def do_bar(s): # Defining do_bar function as a means of printing bar and one argument\n",
" print('bar', s)\n",
"for tag, *args in records: # For statement defines tag (foo or bar) and any other arguments in records as *args\n",
" if tag == 'foo':\n",
" do_foo(*args) # do_foo prints foo and the arguments x and y\n",
" elif tag == 'bar':\n",
" do_bar(*args) #do_bar prints bar and the argument s"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"nobody\n",
"/var/empty\n",
"/usr/bin/false\n"
]
}
],
"source": [
"line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false' # Define line as these strings\n",
"uname, *fields, homedir, sh = line.split(':') # Unpacking strings using .split()\n",
"print(uname) # uname is the first thing before the colon\n",
"print(homedir) # How did we skip to var? By counting backwards from the end\n",
"print(sh) # sh is last substring \n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ACME\n",
"2012\n",
"('ACME', 50, 123.45, (12, 18, 2012))\n"
]
}
],
"source": [
"record = ('ACME', 50, 123.45, (12, 18, 2012)) # Set record equal to a four-element tuple\n",
"name, *_, (*_, year) = record # Everything we want to throw a way, we set to *_ \n",
"print(name) # Should give us first element, 'ACME'\n",
"print(year) # Should give us last element in last tuple, 2012\n",
"print(record) # Should give us first value with starred elements returned"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"[10, 7, 4, 5, 9]\n"
]
}
],
"source": [
"items = [1, 10, 7, 4, 5, 9] # Set items = six list elements\n",
"head, *tail = items # Where head is the first item and *tail is all following items\n",
"print(head)\n",
"print(tail)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"36"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def sum(items): # Creating a function sum with input items\n",
" head, *tail = items # Defining sum of items using star expression for tail\n",
" return head + sum(tail) if tail else head # Sum of all items\n",
"\n",
"sum(items) # Returns sum of head and tail"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.5",
"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.8.5"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "e2f1dc8f986a4de63760f97c74a2a9047e76864767ebd41fc9dd619b409c5d24"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment