Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python equivalent of PHP's compact() and extract()

compact() and extract() are functions in PHP I find tremendously handy. compact() takes a list of names in the symbol table and creates a hashtable with just their values. extract does the opposite. e.g.,

$foo = 'what';
$bar = 'ever';
$a = compact('foo', 'bar');
$a['foo']
# what
$a['baz'] = 'another'
extract(a)
$baz
# another

Is there a way to do the same in Python? I've looked all around and the closest I've come is this thread, which seems to frown on it.

I know about locals(), globals() and vars(), but how can I handily select just a subset of their values?

Does Python have something even better that obviates the need for this?

like image 458
Turadg Avatar asked Apr 23 '09 22:04

Turadg


3 Answers

It's not very Pythonic, but if you really must, you can implement compact() like this:

import inspect

def compact(*names):
    caller = inspect.stack()[1][0] # caller of compact()
    vars = {}
    for n in names:
        if n in caller.f_locals:
            vars[n] = caller.f_locals[n]
        elif n in caller.f_globals:
            vars[n] = caller.f_globals[n]
    return vars

It used to be possible to implement extract() like this, but in modern Python interpreters this doesn't appear to work anymore (not that it was ever "supposed" to work, really, but there were quirks of the implementation in 2009 that let you get away with it):

def extract(vars):
    caller = inspect.stack()[1][0] # caller of extract()
    for n, v in vars.items():
        caller.f_locals[n] = v   # NEVER DO THIS - not guaranteed to work

If you really feel you have a need to use these functions, you're probably doing something the wrong way. It seems to run against Python's philosophy on at least three counts: "explicit is better than implicit", "simple is better than complex", "if the implementation is hard to explain, it's a bad idea", maybe more (and really, if you have enough experience in Python you know that stuff like this just isn't done). I could see it being useful for a debugger or post-mortem analysis, or perhaps for some sort of very general framework that frequently needs to create variables with dynamically chosen names and values, but it's a stretch.

like image 173
David Z Avatar answered Sep 21 '22 21:09

David Z


Is it worth pointing out that extract() (and to a lesser extent, compact()) is one of the most "evil" features of PHP (along with register_globals and eval), and should be avoided?

extract makes it much harder to determine where a variable was defined. When it is harder to trace a variable back to where it was defined, it's harder to check for common security problems like using uninitialized variables, or unfiltered variables which originated from user input.

compact is not as bad, but if used badly can still make it more difficult than it would otherwise be to see where an array member gets set from a variable.

The equivalent of extract() in many other languages is the with keyword. Python now has a with keyword, though it works a bit differently, making it not quite like extract(). However, in other languages such as Javascript, the with keyword also has a poor reputation.

I think the best advice would be to think differently - instead of trying to emulate a bad feature of PHP, think of other ways to do what you want to do with concise and readable code.

like image 33
thomasrutter Avatar answered Sep 19 '22 21:09

thomasrutter


I'm afraid there are no equivalents in Python. To some extent, you can simulate their effect using (and passing) locals:

>>> def compact(locals, *keys):
...     return dict((k, locals[k]) for k in keys)
...
>>> a = 10
>>> b = 2
>>> compact(locals(), 'a', 'b')
{'a': 10, 'b': 2}

>>> def extract(locals, d):
...     for k, v in d.items():
...         locals[k] = v
...
>>> extract(locals(), {'a': 'foo', 'b': 'bar'}
>>> a
'foo'
>>> b
'bar'

Nevertheless, I don't think these functions are "tremendously handy". Dynamic global/local variables are evil and error-prone -- PHP guys learned that when they discouraged register_globals. From my experience, few experienced PHP programmers or major frameworks use compact() or extract().

In Python, explicit is better than implicit:

a = 1
b = 2
# compact
c = dict(a=a, b=b)

# extract
a, b = d['a'], d['b']
like image 36
Ferdinand Beyer Avatar answered Sep 18 '22 21:09

Ferdinand Beyer