Заполните словарь несколькими понятиями

1

Я пытаюсь создать словарь, который выглядит примерно так (частичный пример):

{(1, 1): 'Residential', (2, 1): 'Residential', (3, 1): 'Residential', (1, 2): 'Crafts', (2, 2): 'Crafts', (3, 2): 'Crafts', (4, 1): 'Law, Government', (5, 1): 'Law, Government', (4, 2): 'Public Space', (5, 2): 'Public Space', (6, 1): 'Vice', (6, 2): 'Entertainment'}

Критическая логика, которая кажется проблемой для некоторых более умных решений, состоит в том, что один набор значений распределяется по 3 клавишам, другой набор распространяется по двум клавишам, а третий набор имеет по одному ключу.

Вместо того, чтобы писать дубликаты несколько раз, я предпочел бы создать его программным путем с каким-то пониманием.

USE = dict([(n, 1), "Residential"] for n in range(1, 4))

Это отлично работает, например, для создания первой части. Я мог бы сделать:

USE2 = dict([(n, 2), "Crafts"] for n in range(1, 4))
USE.update(USE2)

Это грязный, неэлегантный и плохой стиль для констант.

Я растерялся за любую другую стратегию.

Я пробовал некоторые формы последовательного понимания:

USE = dict([(n, 1), "Residential"] for n in range(1, 4),
           [(n, 2), "Crafts"] for n in range(1, 4),...

Но это терпит неудачу, потому что dict() не принимает больше одного аргумента.

Я не могу делать распаковки Python 3:

USE = dict(**dict([(n, 1), "Residential"] for n in range(1, 4)),
           **dict([(n, 2), "Crafts"] for n in range(1, 4)),...

Потому что этот стиль ограничен строками по причинам, которые я не понимаю. Использование строк потребует более позднего преобразования случайных чисел, которые будут использоваться для ссылки на dict, и похоже на боль в головной боли (хотя, я полагаю, это зависит от того, есть ли там элегантное решение вообще).

И понимание dict не кажется достаточно прочным (полные данные здесь: где первые 6 в 3 раза чаще, чем последние 6):

space = ["Residential", "Crafts", "Labor", "Shops", "Trade", "Hospitality",
     "Law, Government", "Public Space", "Power", "Manufacture", "Transportation", "Leisure",
     "Vice", "Entertainment", "Storage", "Cultivation", "Academic", "Artists"]
TRY = {(y, x+1): s for x, s in enumerate(space) for y in range(1, 7)}

Это заканчивается тем, что оставляет границы 6x6 из-за размера списка. Если я по модулю x, он переписывает записи. Похоже, мне пришлось бы написать специализированную функцию, чтобы охватить причудливое повторение 3/2/1, которое кажется ненужным для того, что должно быть возможно через какое-то понимание.

Есть ли способ объявить словарь с такой сложностью в одной строке? Если нет, то какой стиль нужно декларировать такой постоянной?

Теги:
dictionary
python-3.6
dictionary-comprehension

4 ответа

1
Лучший ответ

Как говорится:

Не пишите программы, пишите программы, которые пишут программы.

Я бы предложил начать интерактивный сеанс (или IPython), создать частичные dicts и использовать update для их объединения.

Затем распечатайте объединенный dict и вставьте его в исходный код.

  • 0
    Я думаю, что основная причина, по которой я это отклонил, была из-за ремонтопригодности, которая даже не вызывает особого беспокойства, если я действительно думаю об этом. Даже если бы это было так, я мог бы включить процесс конструирования в комментарии, если я когда-нибудь захочу внести изменения, для которых простого поиска / замены было бы недостаточно.
1

2D-структура, которую вы описываете, на самом деле хорошо подходит для массивов numpy:

>>> import numpy as np
>>> 
>>> space = ["Residential", "Crafts", "Labor", "Shops", "Trade", "Hospitality",
...      "Law, Government", "Public Space", "Power", "Manufacture", "Transportation", "Leisure",
...      "Vice", "Entertainment", "Storage", "Cultivation", "Academic", "Artists"]
>>> 
>>> sparr = np.array(space, dtype=object).reshape(3,6).repeat((3,2,1), axis=0)

Обратите внимание, как естественна эта строка: сделайте массив, измените на 3 строки, по шесть столбцов, повторите по вертикали (ось 0), первый элемент 3 раза, второй - дважды и последний раз.

Результат:

>>> sparr
array([['Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       ['Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       ['Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       ['Law, Government', 'Public Space', 'Power', 'Manufacture', 'Transportation', 'Leisure'],
       ['Law, Government', 'Public Space', 'Power', 'Manufacture', 'Transportation', 'Leisure'],
       ['Vice', 'Entertainment', 'Storage', 'Cultivation', 'Academic', 'Artists']], dtype=object)

Это почти правильно, только индексирование основано на нуле, поэтому нам нужно добавить один фиктивный столбец и одну фиктивную строку:

result = np.full((7, 7), None, dtype=object)
>>> result[1:, 1:] = sparr
>>> 
>>> result
array([[None, None, None, None, None, None, None],
       [None, 'Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       [None, 'Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       [None, 'Residential', 'Crafts', 'Labor', 'Shops', 'Trade', 'Hospitality'],
       [None, 'Law, Government', 'Public Space', 'Power', 'Manufacture', 'Transportation', 'Leisure'],
       [None, 'Law, Government', 'Public Space', 'Power', 'Manufacture', 'Transportation', 'Leisure'],
       [None, 'Vice', 'Entertainment', 'Storage', 'Cultivation', 'Academic', 'Artists']], dtype=object)

Теперь это "утка" -equivalent для вашего словаря, ориентированного на поиск. Например:

>>> result[2,1]  # result[(2,1)] also works
'Residential'
  • 0
    Я определенно поиграю с этим, потому что я наслаждаюсь силой numpy даже больше, чем меня пугает.
0

Если вы ищете однострочное понимание, как насчет чего-то подобного?

a = (3,'Residential',3,'Crafts',2,'Law, Government',2,'Public Space',1,'Vice',1,'Entertainment')
use = dict()
use.update(dict([(n / 2 + 1, s), a[n+1]] for s in range(1, 4) for n in range(0, len(a), 2) ))
0

Эта ключевая схема немного загадочна. Здесь один способ немного конденсировать его. Пусть (n0, n1) является ключом для данного названия категории. Для каждого имени n1 является константой, но n0 охватывает смежный диапазон, поэтому мы можем просто сохранить первый и последний элементы этого диапазона. Затем мы используем Dict понимание с двойным for циклы, чтобы расширить сжатую версию в Словарь, что вы на самом деле хотите.

# Condensed key table.
# Each name in the full table has a 2-tuple key (n0, n1)
# The tuples in 'table' are (first_n0, last_n0, n1)
table = {
    'Residential': (1, 3, 1),
    'Crafts': (1, 3, 2),
    'Law, Government': (4, 5, 1),
    'Public Space': (4, 5, 2),
    'Vice': (6, 6, 1),
    'Entertainment': (6, 6, 2),
}

out = {(n0, n1): name for name, (first, last, n1) in table.items()
    for n0 in range(first, last + 1)
}

for k, v in out.items():
    print(f'{k}: {v!r},')

из

(1, 1): 'Residential',
(2, 1): 'Residential',
(3, 1): 'Residential',
(1, 2): 'Crafts',
(2, 2): 'Crafts',
(3, 2): 'Crafts',
(4, 1): 'Law, Government',
(5, 1): 'Law, Government',
(4, 2): 'Public Space',
(5, 2): 'Public Space',
(6, 1): 'Vice',
(6, 2): 'Entertainment',

Здесь эквивалент этого dict comp, использующий традиционные for циклов. Вы можете найти эту версию немного более читаемой.

out = {}
for name, (first, last, n1) in table.items():
    for n0 in range(first, last + 1):
        out[n0, n1] = name
  • 0
    Да, это! Это таблица из RPG, в которой используются броски d6 - и эта таблица использует диапазон результатов (1-3, 4-5, 6) для оси Y.
  • 0
    @Zim Если диапазоны всегда (1-3, 4-5, 6), тогда моя сокращенная таблица может быть упрощена, так как ей не нужно хранить last_n0.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню