Hooked
Hooked

Reputation: 88198

Python: function that returns a dict whose keys are the names of the input arguments

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

Answers (6)

Silas Ray
Silas Ray

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

Eduardo Ivanec
Eduardo Ivanec

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

Fred Foo
Fred Foo

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

Daniel Roseman
Daniel Roseman

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

Óscar López
Óscar López

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

Andrea
Andrea

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

Related Questions