views:

124

answers:

7

I want a function exactly like PHP's compact function.

i.e., if I pass in a list of variable names:

compact('var1','var2')

it returns me back a dict:

{'var1':var1, 'var2':var2}
+3  A: 

The variable name used in the caller is not easily available to the called function. It's probably possible to do with an advanced use of inspect, but it might be better to rethink why you want the variable names.

UPDATE: Now that I know what your real need is ("I want to pass my variables to my template"), the answer is simple: use locals(): it returns a dictionary mapping all your local variable names to their values.

UPDATE 2: How about this?:

def only(d, *keys):
    """Return the subset of dict `d` whose keys are in `keys`."""
    return dict((k,v) for k,v in d.iteritems() if k in keys)

...

a = 17
b = 23
template_data = only(locals(), "a", "b")
Ned Batchelder
I want the variable names because I need to pass them into my template. This is going to be used in a Django project and I'm tired of writing `{'var':var}`. If there is a pattern, it can be codified and reduced.
Mark
Aha! The real need makes for a very simple and Pythonic answer, added above.
Ned Batchelder
@Ned: I guess I wanted a "filtered" `locals()` func, where only the variables you choose will be passed off.
Mark
Yeah... that definitely works too. I know I'm being nitpicky, but I still prefer my sol'n where you don't have to pass in `locals()` at all though.
Mark
Yes, I think your solution fits your criteria best. For use in a template, I'd go with `locals()`: I'm writing the template, so I'm not concerned about extra entries being passed around.
Ned Batchelder
@Ned: Yeah... I might end up doing that too (passing in `locals()` directly). Nevertheless, now we have 2 options. Which will be useful if I ever hire a template designer.
Mark
+1  A: 

The function does not see the names (if any!-) of its actual arguments -- nor in fact any indication whatsoever that those actual arguments are the results of expressions that happen to be single variables rather than constants, function calls, or other expressions of any kind whatsoever.

So, what you require, as you have specified it, is simply not feasible.

Alex Martelli
What if I pass in the names of the variables as strings instead of the actual values? Is there a way to retrieve the values?
Mark
as I understand (a little), python does compilation/check on-the-fly . In that case, there would be a data structure holding the name and/or values of these variable and reducing it to the values, is it possible to get that? or it gets reduced to values before program starts to execute.
Gollum
@Gollum, Python can perfectly well run code from an existing compiled bytecode file (`.pyc` extension) -- if one does not exist, it makes it (or gets the bytecode just in memory if so requested), and does not keep unneeded metainformation around afterwards.
Alex Martelli
@Mark, not without deep "black magic" introspection (over the caller's frame and its locals and globals) that should never be used in production code. Why not accept Python's "explicit is better than implicit" mantra and just call `f(var1=var1, var2=var2)`? Of course the values don't _have_ to be identical to the names, but nothing forbids them to be. Now **that** becomes trivial, it's just `def f(**k): return k`!-) (or f=dict;-). I saw your comment that this "defeats the purpose", but the point is that your purpose cannot be accomplished without unwarranted complications!
Alex Martelli
@Alex: I guess I have different beliefs. I don't believe I should have to type anything where it can be inferred. I believe in convention over configuration. Intelligent defaults that you can override where *necessary*, but not have to do it all over the place.
Mark
@Mark, but the point is, it **cannot** be inferred -- there's nothing intrinsically connecting string `'var1'` to the value `23` (or whatever else it may, or may not, be bound to in a particular moment and scope). You can make your own (hidden, implicit) assumptions (and write fragile, bug-prone, version-dependent deep introspection code to support such mysterious assumptions, code that should never go into production in any sane world!), but that's not the kind of simple, robust, powerful code that production programmers are happy to write and maintain.
Alex Martelli
@Alex: I'm employing the convention that the same variable names used in the view function should also be used in the template for consistency. Consistency is good, no? By using the `compact()` function you're preventing developers from (perhaps unintentionally) renaming the variables as they're passed off. Anyway, I'm giving people *flexibility* here. There's nothing that says they *have* to use this function; they can continue to use `{'x':x}` or even `{'x':y}` if they are in your camp.
Mark
A: 

if you have a list called args and you want to put it into a dictionary called kwargs as in your example:

for k,v in enumerate(args):
    kwargs["var%d"%(k+1)] = v

the keys are literally the index of the items in args, though; this can't be useful?

Will
Eh? My variables won't actually be named like that
Mark
+1  A: 

(1) Python is not PHP. (2) "variable name lost when passed into a function" is true.

Do Not Use Variables for this.

Do something simpler. Use the dict function.

>>> dict( var1=1, var2='a', var3=1+3j )
{'var1': 1, 'var3': (1+3j), 'var2': 'a'}

That seems to do what you want. The variable "names" are in there exactly once. You don't really need to create other local, temporary variables.

S.Lott
This defeats the purpose. I'm trying to *avoid* writing `dict(var1=var1)` or `{'var1':var1}`.
Mark
@S.Lott, can you read my question that I asked in @Alex Martelli's answer. if variable names are lost then there has to be some mechanism which gives value for any variable-name? am I misunderstanding something.I m also assuming that compilation/execution is one/nonseperable in python.
Gollum
@Gollum: I have no idea what's "lost" when a variable is evaluated and the object (not the variable) continues through the evaluation of the function. What's "lost"? The variable is evaluated to find the object, the object persists through the life of the calculation. The object endures until there are no references to it. What's "lost?" What are you talking about?
S.Lott
@S.Lott, thanks for your time. read the answer Alex Martelli gave. I meant the meta-information. like variable_name --> value
Gollum
@Gollum: the value exists independently from all the various variable names. I have no idea how it can be otherwise or what the question could even be. The discussion in Alex's answer is hard to follow because everyone's talking about some completely magical and senseless connection between variables and objects.
S.Lott
@S.Lott, by *variable_name*, I mean foo ( *variable_name* ). Hence, this name should be present somewhere at runtime? <-- *this was my question* **@Alex** answered that no _Python can perfectly well run code from an existing compiled bytecode file (.pyc extension) -- if one does not exist, it makes it (or gets the bytecode just in memory if so requested), and does not keep unneeded metainformation around afterwards._
Gollum
A: 

This is awkward and has limits, but for the rest it works

def p(*args):
  p = {}
  for i in args:   
     exec 'p["%s"] = %s' % (i, i)
  return p
ShinTakezou
not in time I have this cr**y solution and the OPer posted something better that works basically the same! :/
ShinTakezou
This is less safe than what I posted in my question (updated) and has the same problem.
Mark
in fact, noticed that:) (both the fact that yours is better and that has the same problem; but I had alrady wrote it before seeing the update)
ShinTakezou
+1  A: 
>>> import inspect
>>> def compact(*args):
...     d = {}
...     for a in args:
...         d[a] = inspect.stack()[1][0].f_locals[a]
...     return d
...
>>> def test():
...     x = 5
...     print compact('x')
...
>>> test()
{'x': 5}
Mark
+1  A: 

With the help of Getting method parameter names in python, here you go with decorator solution, so that you are able to apply it to any function:

def compactor(func):
    import inspect
    def compactor_impl(*args, **kwargs):
        arg_names = inspect.getargspec(func)[0]
        return dict(zip(arg_names, args))
    return compactor_impl

@compactor
def package(var1, var2, var3):
    pass

@compactor
def package2(var1, var2, var3, supervar):
    pass

assert package(1, 2, 3) == {'var1': 1, 'var2': 2, 'var3': 3,}
assert package2(1, 2, 3, 'boh') == {'var1': 1, 'var2': 2, 'var3': 3, 'var3': 3, 'supervar': 'boh'}

I assumed that the functions do nothing else but return the name-value pairs. You might want to add some checks (asserts) to ensure that len(args) == len(arg_names) and that kwargs is empty, because this will not be checked once the decorator is applied.

van
Nice...but I think you missed the intent.
Mark