In the pursuit of some nice-looking, functional-feeling Python code, I'm wondering how to bind a set of names to the result of applying a function to their currently bound values. Consider this code:
# preamble
a = [4]
b = [5]
c = [6]
# code
a = len(a)
b = len(b)
c = len(c)
print(a, b, c)
However, typing x = len(x)
for each of the three variables must surely be redundant, right?
I can reassign the names like this instead:
a, b, c = (len(x) for x in (a, b, c))
... but this also carries some redundancy, because I had to list a, b, c
twice!
We can use inspect
to make things a bit better:
import inspect
def apply_in_scope(f, v):
parent = inspect.stack()[1].frame.f_globals
for i in v:
parent[i] = f(parent[i])
a = [5]
b = [7, 9]
c = [9, 10, 11, 12]
apply_in_scope(len, ("a", "b", "c"))
print(a, b, c)
... but now I have to put the names of my variables in quotes, instead of using them directly. The problem is that when you call a function in Python, the function gets passed a new reference.
Changing the code slightly, we can do a quadratic search for arguments that match the given parameters, which is very slow, but I believe works:
import inspect
def apply_in_scope(f, values):
parent = inspect.stack()[1].frame.f_globals
names = [k for k, v in parent.items() if any(v is x for x in values)]
for name in names:
parent[name] = f(parent[name])
a = [5]
b = [7, 9]
c = [9, 10, 11, 12]
apply_in_scope(len, (a, b, c))
print(a, b, c)
... but I can't help thinking there must be a better way, or that I'm missing something simple. This would be reasonably easy in LISP, which makes me think it shouldn't require so much gymnastics in Python.
(warning: the above solution will apply the function multiple times if more than one variable is
the same value!)
Edit: this question is distinct from how to make a variable number of variables because that question (1) applies to the global scope and not the local function scope and (2) does not solve the basic meta-programming question asked here of reassigning and applying a function.