I am using a long ad hoc script for exploratory data analysis -- not for tool development. The script has gotten quite long, so I've taken to del
ing ephemeral variables to keep the Spyder Variable Explorer clean. I've done this all over the script.
I tried to be streamline the script by coding some loops as tuple comprehensions, thus eliminating the need for an extra line of code to del
the iteration variable. Here is an example of three ways to iterate through figures to clear the plots:
# Generate the figures
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.close('all')
plt.scatter([1,2,3],[4,5,6])
plt.figure()
plt.scatter([1,2,3],[6,5,4])
# 3 ways to clear the figures
if True: # Use Tuple Comprehension
( plt.figure(iFig).clf() for iFig in plt.get_fignums() )
elif True: # Use List Comprehension
[ plt.figure(iFig).clf() for iFig in plt.get_fignums() ]
else: # Don't use comprehension
for iFig in plt.get_fignums(): plt.figure(iFig).clf()
del iFig # The extra line of source code I want to avoid
The 3rd and final option is the one I've been using. The 1st and 2nd options are my attempts at tuple comprehension and list comprehension.
The tuple comprehension returns a generator object and doesn't actually execute evaluate the invocation to clf
unless I assign the tuple to x
and execute next(x)
twice. I can see the figures clearing each time.
This is unnecessary for the list comprehension.
I started off with tuple comprehension with the rationale that I can avoid using a mutable container if I'm not going to use the contents anyway.
Why does the tuple comprehension yield a generator that must be iterated through while list comprehension evaluates all the elements needed to build the list right away?
Is there a known Python rule about list and tuple comprehension that would have allowed me to foresee this?
This is not a "tuple comprehension":
( plt.figure(iFig).clf() for iFig in plt.get_fignums() )
It's just a generator wrapped in parentheses. They're like parentheses around any other expression, for syntactic grouping, they don't create a tuple. Consider the expression (1)
-- this is equivalent to just 1
, not (1,)
.
Tuples are created using a comma, not parentheses, although often we wrap the comma-separated items in parentheses to make the grouping clear. This is specifically necessary when the tuple is an argument to a function, because the comma interpretation as argument separators takes precedence. Consider:
x = 1, 2 # sets x to a tuple
If you want to create a tuple from the generator, call the tuple()
function:
tuple(plt.figure(iFig).clf() for iFig in plt.get_fignums())
But stylistically, this is inappropriate in the first place (i.e. it's not considered Pythonic). Generators and comprehensions shouldn't be used purely for side effects, use them when you actually want to process the results (either lazily with a generator, or collecting them with a comprehension). Use a for
loop when you only care about side effects.