Python - Использование предыдущих значений в словаре - PullRequest
0 голосов
/ 05 ноября 2018

Есть ли способ сделать что-то подобное

dict = {
    'a':1,
    'b':dict['a']+1,
}

или я должен сначала создать значения где-нибудь еще, а затем создать словарь.

Ответы [ 4 ]

0 голосов
/ 05 ноября 2018

Обычно одно использование defaultict для чего-то вроде

d = defaultdict(lambda: 1, {"a": 0})

Но можно получить более забавные определения:

from collections import defaultdict
import itertools

d = defaultdict(itertools.count().__next__)
print (d['a'], d['b'])
0 голосов
/ 05 ноября 2018

Если приращение всегда равно 1, вы можете использовать перечисление:

keys = ['a', 'b', 'c']
result = {k: i for i, k in enumerate(keys, 1)}
print(result)

выход

{'b': 2, 'c': 3, 'a': 1}

Если приращение является пользовательским, вы можете использовать накапливать с add :

from itertools import accumulate
from operator import add

keys = ['a', 'b', 'c']
increments = [1, 2, 7]

result = {k: v for k, v in zip(keys, accumulate(increments, add))}
print(result)

Обратите внимание, что первое значение в приращениях соответствует значению первого ключа.

UPDATE

Как уже упоминалось @tobias_k, вы можете использовать накопление, не передавая add:

result = {k: v for k, v in zip(keys, accumulate(increments))}
0 голосов
/ 05 ноября 2018

Вот довольно глупый подход. Определите ваш литерал dict в строке, а затем используйте ast.parse, чтобы получить значение каждого выражения, определяющего его ключи и значения. Затем вы можете оценить каждый из них по очереди, предоставив пользовательский аргумент locals для eval, чтобы принудительно сделать объект словаря доступным, пока словарь еще создается.

import ast

def eval_self_referential_dict_literal(s, locals = None):
    """
    Evaluates a string containing a dict literal.
    Capable of resolving keys and values that are present higher up in the literal. Use `d` to refer to the dict itself.
    Provide a mapping to the `locals` parameter if you need to access other names in the literal. You might pass in `locals()`, for example.
    """

    expr = ast.parse(d, mode='eval')
    assert isinstance(expr, ast.Expression)
    dict_node = expr.body
    assert isinstance(dict_node, ast.Dict)

    result = {}
    if locals is None:
        locals = {}
    else:
        locals = locals.copy()
    locals['d'] = result

    for key_node, value_node in zip(dict_node.keys, dict_node.values):
        key = eval(compile(ast.Expression(key_node), '', mode='eval'), globals(), locals)
        value = eval(compile(ast.Expression(value_node), '', mode='eval'), globals(), locals)
        result[key] = value
    return result

d = """
{
    'a':1,
    'b':d['a']+1,
    d['b']: d['a'],
    x: 99
}
"""

x = "blah"

print(eval_self_referential_dict_literal(d, locals()))

Результат:

{'a': 1, 'b': 2, 2: 1, 'blah': 99}
0 голосов
/ 05 ноября 2018

Ваш код не будет работать, поскольку переменная dict (которая, кстати, скрывает встроенную функцию dict) равна , пока не определена до того, как литерал {...} был оценен , поэтому его нельзя использовать в пределах {...}.

Вместо этого вы можете создать итерацию чисел, например, используя itertools.count или просто iter(range(1, some_number)), а затем получите значения next из этой итерации.

>>> nums = itertools.count(1)
>>> {"a": next(nums), "b": next(nums), "c": next(nums)}
{'a': 1, 'b': 2, 'c': 3}

Или используйте функцию dict с zip, чтобы объединить список ключей с их значениями:

>>> dict(zip(("a", "b", "c"), itertools.count(1)))
{'a': 1, 'b': 2, 'c': 3}

(Это также работает с шагами, отличными от 1, или фактически с произвольными списками значений, а не только с числами. В случае шагов всегда 1, enumerate может действительно быть более простой альтернативой.)

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