Python中PHP的compact()和extract()的等价实现

14 投票
6 回答
9109 浏览
提问于 2025-04-15 11:13

compact() 和 extract() 是 PHP 里的两个非常实用的函数。compact() 函数可以从当前的变量列表中提取出变量名,并把它们的值放到一个哈希表里。extract() 则是做相反的事情。

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

有没有办法在 Python 中做到同样的事情呢?我查了很多资料,最接近的就是这个讨论,但看起来大家对这个不太赞同。

我知道有 locals()、globals() 和 vars() 这些函数,但我该怎么方便地选择它们的某一部分值呢?

Python 有没有更好的方法,可以省去这个需求呢?

6 个回答

9

值得一提的是,extract()(还有稍微不那么糟糕的compact())是PHP中最“邪恶”的特性之一(还有register_globalseval),应该尽量避免使用。

extract会让你很难确定一个变量是在哪里定义的。当你很难追踪一个变量的来源时,就更难检查一些常见的安全问题,比如使用未初始化的变量,或者来自用户输入的未经过滤的变量。

compact虽然没有那么糟糕,但如果使用不当,仍然会让你更难看出一个数组成员是从哪个变量设置的。

在许多其他编程语言中,extract()的类似功能是with关键字。Python现在也有with关键字,虽然它的工作方式有点不同,所以和extract()不完全一样。不过在其他语言,比如JavaScript,with关键字的名声也不太好。

我认为最好的建议是换个思路——与其试图模仿PHP的这个糟糕特性,不如想想其他方法,用简洁易读的代码实现你想要的功能。

10

我觉得在Python里没有类似的东西。虽然你可以通过使用并传递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'

不过,我认为这些函数并不是“特别好用”。动态的全局或局部变量其实是有问题的,容易出错——PHP的开发者们在不鼓励使用register_globals时就已经意识到了这一点。根据我的经验,很少有经验丰富的PHP程序员或大型框架会使用compact()extract()

在Python中,明确的比隐含的要好

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

# extract
a, b = d['a'], d['b']
14

这段代码虽然不是很符合Python的风格,但如果你真的需要的话,可以这样实现compact()

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

以前可以这样实现extract(),但在现代的Python解释器中,这种方法似乎不再有效(其实它本来就不应该工作,只是2009年的实现有些奇怪,让你可以这样做):

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

如果你真的觉得需要使用这些函数,那你可能是在做一些不太对的事情。这似乎和Python的哲学相悖,至少有三点:“显式优于隐式”,“简单优于复杂”,“如果实现很难解释,那就是个坏主意”,可能还有更多(其实,如果你在Python方面有足够的经验,你会知道这样的做法是不被推荐的)。我能理解在调试或事后分析中可能会用到这些,或者在某种非常通用的框架中,频繁需要创建动态命名和赋值的变量,但这确实有点牵强。

撰写回答