Skip to content

Instantly share code, notes, and snippets.

@langford
Last active February 7, 2018 14:32
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 langford/2769da7d8f1ab6074c98d0c22c2a2f6f to your computer and use it in GitHub Desktop.
Save langford/2769da7d8f1ab6074c98d0c22c2a2f6f to your computer and use it in GitHub Desktop.
Explanations of different comprehensions in python3
"""This is an explainer for what list comprehensions are
Python was more adopted by mathy types than functional programmer types of people.
One thing that mathy types and functional programmer types have in common is
they often think similarly about data.
They both use statements about varables, called 'Declarative programming' over
statements that are a series of instructions to the compiler to move a value
here or there or whatever (called 'Imperative programming')
In python, the following is a valid piece of code for transforming a collection
of things, into another collection"""
def transformation(a):
"""Arbitrary transformation, can be any change of the input value"""
return a+a
def predicate(a):
"""A predicate is a function or expression which returns a true or false value from a given input"""
return len(a) > 3
# old_things to new_things for loop style
old_things = ["it", "doesn't", "matter", "for", "this", "demonstration"]
new_things = []
for each_old_thing in old_things:
if predicate(each_old_thing):
a_new_thing = transformation(each_old_thing)
new_things.append( a_new_thing )
print("old_things",old_things,"new_things (for-each loop)",new_things)
# List comprehensions are **just shorthand** for this *common pattern*. Use of
# them often regularizes your code in a way that makes it so you do not
# accidentally leave out one of the common steps.
# This next statement says the *exact same thing the code before it said...but does it as a list comprehension
# old_things to new_things list comprehension style
new_things = [transformation(each_old_thing) for each_old_thing in old_things if predicate(each_old_thing)]
print("old_things",old_things,"new_things (predicate)",new_things)
# Comprehensions can omit the predicate. This makes them analogous to the "map" function in many modern languages.
# It changes things from one domain, to another. This is useful when going from indexes to the items in a list,
# or when doing something like "lists, being turned into their sums"
def some_operations_to_move_to_new_domain(item):
return item + item
things_in_old_domain = [1, 3, 4, 5, "hooooooooo"]
things_in_new_domain = [some_operations_to_move_to_new_domain(item) for item in things_in_old_domain]
print(things_in_new_domain)
# Comprehensions can instead "omit" the transformation by just specifying the same "item" as was extracted in the for
# statement, while leaving the predicate in. This makes them analogous to other languages's "Filter" function
loosely_specified_items = [2,6,8,10, "hooooooooohooooooooo"]
more_tightly_specified_items = [item for item in loosely_specified_items if type(item) == int]
print("filtered", more_tightly_specified_items)
#comprehensions can even work over multidimensional data. Here is a 2D example
#for loop style
multi_dimensional_old_things = [["it", "doesn't", "matter", "for", "this", "demonstration"],
["but", "this", "is", "madness"]]
new_things = []
for each_old_collection in multi_dimensional_old_things:
for each_old_thing in each_old_collection:
if predicate(each_old_thing):
a_new_thing = transformation(each_old_thing)
new_things.append( a_new_thing )
print("new things from written out for loops",new_things)
new_things = [transformation(each_old_thing) for each_old_collection in multi_dimensional_old_things for each_old_thing in each_old_collection if predicate(each_old_thing)]
print("new things as a list comprehension :", new_things)
# If one gets too long, you can format it a bit like this for readability, or you can break it into multiple actions
new_things = [transformation(each_old_thing)
for each_old_collection in multi_dimensional_old_things
for each_old_thing in each_old_collection
if predicate(each_old_thing)]
# a short mention on enumerate:
# enumerate is basically the following, renamed to not clash with python's named function
vec = [32.3, 44.3, 0 ,2 ,3 ,4]
import math
def enumerate_basically(vec):
z = []
for i in vec:
return zip(i,vec[i])
# therefore "for idx,val in enumerate(vec)" is shorthand for
for p in enumerate(vec):
idx = p[0]
val = p[1]
# That's called destructuring in some languages. I'm not sure python calls it anything.
# Now that you know enumerate, here is a familiar example that demos dictionary comprehensions:
compressed_dict = {}
for idx,val in enumerate(vec):
if not math.isclose(0,val):
compressed_dict[idx+44]=val*2
# Rewritten as a dictionary comprehension
#The syntax is {key:value for item in sequence if predicate}
# Notice the curly braces *and* the idx:val syntax at the front.
# Those two features identify it as a *dictionary* comprehension
compressed_dict = {idx+44:val*2 for idx,val in enumerate(vec) if not math.isclose(0,val)}
print("compressed_dict", compressed_dict)
#written on multiple lines for clarity
compressed_dict = {idx+44:val*2
for idx,val in enumerate(vec)
if not math.isclose(0,val)}
# If you slightly mess up the dictionary comprehension syntax, you might accidentally write a set comprehension.
# If you only return a single value rather than the idx:val,
# curly braces on a comprehension mean "return a set"
# (a set in computing is an unordered group of unique objects)
compressed_set = {val*2 for idx,val in enumerate(vec) if not math.isclose(0,val)}
print("compressed_set",compressed_set)
# I only mention "set" here so if you accidentally get one, you know what happened. They look a
# little bit like a dict and a little bit like an array so can be easily mistaken for them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment