Reputation: 88198
Is it possible to write a function f
that takes an arbitrary tuple of mixed data:
T = 1.0
N = 20
L = 10
args = (T,N,L)
f(*args) # or maybe f(T,N,L)?
and returns as output:
{'T':1.0, 'N':20, 'L':10}
There is a related question using local
, but I seem to lose the names once they are passed to the function. Is there a way to prevent this? I'm guessing that the variables are being passed by value and thus they are considered new objects.
Followup: Python: Using a dummy class to pass variable names?
Upvotes: 2
Views: 8782
Reputation: 26160
Yeah, as best I know, args is a list. To preserve label names, you'd need to pass a dict to **kwargs, and explicitly or programaticly set the names using local
.
You could do something like:
def filter_locals(caller_locals, *args):
filtered = {}
for key in args:
filtered[key] = caller_locals[key]
return filtered
Then:
X = 1
Y = 2
func(filter_locals(locals(), 'X', 'Y')
Where func processes the arbitrary length list as kwargs.
Upvotes: 0
Reputation: 11862
A solution using globals()
. This will only work if the values are unique in the module scope, so it's fragile - if there are duplicates, it will choose the first name that points to that value, somewhat arbitrarily. And yes, you're probably better off not doing this anyway and rethinking the problem instead.
def f(*names):
r = {}
for n in names:
r[[ name for name in globals() if globals()[name] is n ][0]] = n
return r
T = 1.0
N = 20
L = 10
print f(T,N,L)
{'T': 1.0, 'L': 10, 'N': 20}
Upvotes: 1
Reputation: 363737
No, this is not possible in general with *args
. You'll have to use keyword arguments:
>>> def f(**kwargs):
... return kwargs
...
>>> f(T=1.0, N=20, L=10)
{'T': 1.0, 'L': 10, 'N': 20}
The reason is that *args
does not introduce names for the individual arguments; it only introduces the name args
for the whole list of them. The function has no insight into how what names, if any, the arguments have outside of it.
When the number of arguments is fixed, you can do this with locals
:
>>> def g(T, N, L):
... return locals()
...
>>> g(T=1.0, N=20, L=10)
{'L': 10, 'T': 1.0, 'N': 20}
(or explicitly with return {'T': T, 'N': N, 'L': L}
.)
Upvotes: 3
Reputation: 599788
Your guess is unfortunately wrong, and in fact the whole question betrays a misunderstanding of how Python names work.
Python objects, on the whole, don't know their names. That's completely understandable when you consider that you can easily assign a=b
, and now a
and b
both refer to exactly the same object. Python's "variables" are simply names that point to objects, they do not contain those objects in any real way. When you pass arguments to a function, you're passing the underlying objects, not the names. The receiving function simply binds them to the names specified in the function signature - or, in your case, simply keeps them in args
without giving them any names at all.
So, when you're asking how to get the names that objects had in a different scope, you can't ask the objects themselves. There probably is a way to do this, but it involves mucking about with inspecting calling frames, and I very much doubt you want to.
Upvotes: 2
Reputation: 236114
It'd be necessary to pass the keys and the values as separate arguments:
keys = ('T', 'N', 'L')
values = (1.0, 20, 10)
d = dict(zip(keys, values))
In your code, the line args = (T,N,L)
evaluates to args = (1.0, 20, 10)
, the "names" of the variables T, N, L are replaced by their values.
Upvotes: 0
Reputation: 20493
In Python variables are just labels attached to values. So there is no way to know, from another scope, what labels you were using when calling the function.
Upvotes: 2