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?
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.
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.
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']
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With