Python-эквивалент PHP для compact () и extract () - PullRequest
7 голосов
/ 24 апреля 2009

compact () и extract () - функции в PHP, которые я считаю чрезвычайно удобными. compact () берет список имен в таблице символов и создает хеш-таблицу только с их значениями. извлечение делает обратное. например.,

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

Есть ли способ сделать то же самое в Python? Я огляделся по сторонам, и самое близкое, что я получил, это эта нить , которая, кажется, хмурится.

Я знаю о locals (), globals () и vars (), но как мне легко выбрать только подмножество их значений?

Есть ли в Python что-то еще лучше, что устраняет необходимость в этом?

Ответы [ 6 ]

10 голосов
/ 24 апреля 2009

Это не очень Pythonic, но если вы действительно должны:

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

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

Я довольно часто использовал эти реализации, и они работают, но техническая модификация f_locals не поддерживается.

Если серьезно, если вы действительно чувствуете необходимость использовать эти функции, вы, вероятно, делаете что-то неправильно. Кажется, он противоречит философии Python как минимум по трем причинам: «явный лучше, чем неявный», «простой лучше, чем сложный», «если реализацию трудно объяснить, это плохая идея», возможно больше (и действительно, если у вас достаточно опыта в Python, вы знаете, что такие вещи просто не сделаны). Я мог бы увидеть, что это полезно для отладчика или анализа после смерти, или, возможно, для какой-то очень общей структуры, которая часто должна создавать переменные с динамически выбранными именами и значениями, но это растяжение.

Если вы собираетесь использовать эти функции, вы должны по крайней мере хранить переменные extract, содержащиеся в небольших областях. Оберните их в функции, которые вы можете затем считать «черными ящиками». Основная причина extract плохая в том, что она помещает переменные в вашу таблицу символов таким образом, который непонятен при проверке кода. Если вы сохраните влияние этих переменных на очень маленькую функцию и объясните, что вы делаете, с помощью чистого кода и комментариев, это не такая уж большая проблема.

9 голосов
/ 24 апреля 2009

Боюсь, в 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']
6 голосов
/ 24 апреля 2009

Стоит ли указывать, что extract() (и в меньшей степени compact()) является одной из самых "злых" возможностей PHP (наряду с register_globals и eval), и ее следует избегать

extract значительно затрудняет определение того, где была определена переменная. Когда сложнее отследить переменную до того места, где она была определена, сложнее проверить наличие общих проблем безопасности, таких как использование неинициализированных переменных или нефильтрованных переменных, которые возникли из пользовательского ввода.

compact не так уж и плохо, но при неправильном использовании все равно может усложнить задачу, чем в противном случае увидеть, где член массива устанавливается из переменной.

Эквивалентом extract() во многих других языках является ключевое слово with. В Python теперь есть ключевое слово with, хотя оно работает немного по-другому, что делает его совсем не похожим на extract(). Однако в других языках, таких как Javascript, ключевое слово with также имеет плохую репутацию .

Я думаю, что лучший совет - думать иначе - вместо того, чтобы пытаться эмулировать плохую функцию PHP, подумайте о других способах делать то, что вы хотите, с помощью краткого и читабельного кода.

3 голосов
/ 14 мая 2009

Компактная функция PHP в Python (работает с 2.6; работа с более ранними версиями Python не гарантируется):

import inspect
def compact(*args):
    return dict([(i, inspect.currentframe().f_back.f_locals.get(i, None)) 
                  for i in args])

Я написал об этом более подробно: Python может быть таким же уродливым, как PHP .

2 голосов
/ 24 апреля 2009

Я предполагаю, что эквивалент extract($x) равен globals().update(x), а для compact() это подмножество vars()

>>> foo, bar, baz = 1, 2, 3
# extract
>>> globals().update({"foo": 4, "qux": 5})
>>> foo
4
>>> qux
5
# compact
>>> d = dict((k, v) for k, v in vars().iteritems() if k in ["foo", "bar"])
>>> d
{'bar': 2, 'foo': 1}
1 голос
/ 23 сентября 2016

Вы можете сделать это (хотя я бы рекомендовал только экономно ...) с помощью функции locals(), которая возвращает обновляемый dict. Пример:

$ python
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
>>> locals().update({'derek':'anderson'})
>>> derek
'anderson'
>>> 

Так что locals() будет "компактным всем", а locals().update() будет extract.

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...