Разное

Словарей генератор: Все о выражениях-генераторах, генераторах списков, множеств и словарей / Хабр

Содержание

Все о выражениях-генераторах, генераторах списков, множеств и словарей / Хабр

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

Эта статья глубже и детальней предыдущих и поэтому может быть интересна не только новичкам, но и достаточно опытным Python-разработчикам.

Будут рассмотрены: выражения-генераторы, генераторы списка, словаря и множества, вложенные генераторы (5 вариантов), работа с enumerate(), range().

А также: классификация и терминология, синтаксис, аналоги в виде циклов и примеры применения.

Я постарался рассмотреть тонкости и нюансы, которые освещаются далеко не во всех книгах и курсах, и, в том числе, отсутствуют в уже опубликованных на Habrahabr статьях на эту тему.

Оглавление:

1. Определения и классификация.
2. Синтаксис.
3. Аналоги в виде цикла for и в виде функций.
4. Выражения-генераторы.
5. Генерация стандартных коллекций.
6. Периодичность и частичный перебор.
7. Вложенные циклы и генераторы.
8. Использование range().
9. Приложение 1. Дополнительные примеры.
10. Приложение 2. Ссылки по теме.

1. Определения и классификация

1.1 Что и зачем

  • Генераторы выражений предназначены для компактного и удобного способа генерации коллекций элементов, а также преобразования одного типа коллекций в другой.
  • В процессе генерации или преобразования возможно применение условий и модификация элементов.
  • Генераторы выражений являются синтаксическим сахаром и не решают задач, которые нельзя было бы решить без их использования.

1.2 Преимущества использования генераторов выражений

  • Более короткий и удобный синтаксис, чем генерация в обычном цикле.
  • Более понятный и читаемый синтаксис чем функциональный аналог сочетающий одновременное применение функций map(), filter() и lambda.
  • В целом: быстрее набирать, легче читать, особенно когда подобных операций много в коде.

1.3 Классификация и особенности

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

В данной статье используются следующие обозначения:

  • выражение-генератор (generator expression) — выражение в круглых скобках которое выдает создает на каждой итерации новый элемент по правилам.
  • генератор коллекции — обобщенное название для генератора списка (list comprehension), генератора словаря (dictionary comprehension) и генератора множества (set comprehension).

В отдельных местах, чтобы избежать нагромождения терминов, будет использоваться термин «генератор» без дополнительных уточнений.

2. Синтаксис

Для начала приведем иллюстрацию общего синтаксиса выражения-генератора.
Важно: этот синтаксис одинаков и для выражения-генератора и для всех трех типов генераторов коллекций, разница заключается, в каких скобках он будет заключен (смотрите предыдущую иллюстрацию).

Общие принципы важные для понимания:

  • Ввод — это итератор — это может быть функция-генератор, выражение-генератор, коллекция — любой объект поддерживающий итерацию по нему.
  • Условие — это фильтр при выполнении которого элемент пойдет в финальное выражение, если элемент ему не удовлетворяет, он будет пропущен.
  • Финальное выражение — преобразование каждого выбранного элемента перед его выводом или просто вывод без изменений.

2.1 Базовый синтаксис

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]    # Пусть у нас есть исходный список
list_b = [x for x in list_a]           # Создадим новый список используя генератор списка
print(list_b)                          # [-2, -1, 0, 1, 2, 3, 4, 5]
print(list_a is list_b)                # False - это разные объекты!

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

Мощь генераторов выражений заключается в том, что мы можем задавать условия для включения элемента в новую коллекцию и можем делать преобразование текущего элемента с помощью выражения или функции перед его выводом (включением в новую коллекцию).

2.2 Добавляем условие для фильтрации

Важно: Условие проверяется на каждой итерации, и только элементы ему удовлетворяющие идут в обработку в выражении.

Добавим в предыдущий пример условие — брать только четные элементы.

# if x % 2 == 0 - остаток от деления на 2 равен нулю - число четное
list_a = [-2, -1, 0, 1, 2, 3, 4, 5] 
list_b = [x for x in list_a if x % 2 == 0]
print(list_b)   # [-2, 0, 2, 4]

Мы можем использовать несколько условий, комбинируя их логическими операторами:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x for x in list_a if x % 2 == 0 and x > 0]
# берем те x, которые одновременно четные и больше нуля
print(list_b)   # [2, 4]

2.3 Добавляем обработку элемента в выражении

Мы можем вставлять не сам текущий элемент, прошедший фильтр, а результат вычисления выражения с ним или результат его обработки функцией.

Важно: Выражение выполняется независимо на каждой итерации, обрабатывая каждый элемент индивидуально.

Например, можем посчитать квадраты значений каждого элемента:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x**2 for x in list_a]
print(list_b)   # [4, 1, 0, 1, 4, 9, 16, 25]

Или посчитать длины строк c помощью функции len()

list_a = ['a', 'abc', 'abcde']
list_b = [len(x) for x in list_a]
print(list_b)   # [1, 3, 5]

2.4 Ветвление выражения

Обратите внимание: Мы можем использовать (начиная с Python 2. 5) в выражении конструкцию if-else для ветвления финального выражения.

В таком случае:

  • Условия ветвления пишутся не после, а перед итератором.
  • В данном случае if-else это не фильтр перед выполнением выражения, а ветвление самого выражения, то есть переменная уже прошла фильтр, но в зависимости от условия может быть обработана по-разному!
list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x if x < 0 else x**2 for x in list_a]
# Если x-отрицательное - берем x, в остальных случаях - берем квадрат x
print(list_b)   # [-2, -1, 0, 1, 4, 9, 16, 25]

Никто не запрещает комбинировать фильтрацию и ветвление:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = [x**3 if x < 0 else x**2 for x in list_a if x % 2 == 0]
# вначале фильтр пропускает в выражение только четные значения
# после этого ветвление в выражении для отрицательных возводит в куб, а для остальных в квадрат
print(list_b)   # [-8, 0, 4, 16]

Этот же пример в виде цикла

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_b = []
for x in list_a:
    if x % 2 == 0:
        if x < 0:
            list_b.append(x ** 3)
        else:
            list_b.append(x ** 2)
print(list_b)   # [-8, 0, 4, 16]

2.5 Улучшаем читаемость

Не забываем, что в Python синтаксис позволяет использовать переносы строк внутри скобок. Используя эту возможность, можно сделать синтаксис генераторов выражений более легким для чтения:

numbers = range(10)

# Before
squared_evens = [n ** 2 for n in numbers if n % 2 == 0]

# After
squared_evens = [
    n ** 2
    for n in numbers
    if n % 2 == 0
]

3. Аналоги в виде цикла for и в виде функций

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

Для примера возьмем простую задачу — сделаем из списка чисел список квадратов четных чисел и решим ее с помощью трех разных подходов:

3.

1 Решение с помощью генератора списка

numbers = range(10)
squared_evens = [n ** 2 for n in numbers if n % 2 == 0]
print(squared_evens)   # [0, 4, 16, 36, 64]

3.2. Решение c помощью цикла for

Важно: Каждый генератор выражений можно переписать в виде цикла for, но не каждый цикл for можно представить в виде такого выражения.

numbers = range(10)
squared_evens = []
for n in numbers:
    if n % 2 == 0:
        squared_evens.append(n ** 2)
print(squared_evens)   # [0, 4, 16, 36, 64]

В целом, для очень сложных и комплексных задач, решение в виде цикла может быть понятней и проще в поддержке и доработке. Для более простых задач, синтаксис выражения-генератора будет компактней и легче в чтении.

3.3. Решение с помощью функций.

Для начала, замечу, что выражение генераторы и генераторы коллекций — это тоже функциональный стиль, но более новый и предпочтительный.

Можно применять и более старые функциональные подходы для решения тех же задач, комбинируя map(), lambda и filter().

numbers = range(10)
squared_evens = map(lambda n: n ** 2, filter(lambda n: n % 2 == 0, numbers))
print(squared_evens)         # <map object at 0x7f661e5dba20>
print(list(squared_evens))   # [0, 4, 16, 36, 64]
# Примечание: в Python 2 в переменной squared_evens окажется сразу список, а в Python 3 «map object», который мы превращаем в список с помощью list()

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

4. Выражения-генераторы

Выражения-генераторы (generator expressions) доступны, начиная с Python 2.4. Основное их отличие от генераторов коллекций в том, что они выдают элемент по-одному, не загружая в память сразу всю коллекцию.

UPD: Еще раз обратите внимание на этот момент: если мы создаем большую структуру данных без использования генератора, то она загружается в память целиком, соответственно, это увеличивает расход памяти Вашим приложением, а в крайних случаях памяти может просто не хватить и Ваше приложение «упадет» с MemoryError. В случае использования выражения-генератора, такого не происходит, так как элементы создаются по-одному, в момент обращения.

Пример выражения-генератора:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
my_gen = (i for i in list_a)    # выражение-генератор
print(next(my_gen))     # -2 - получаем очередной элемент генератора
print(next(my_gen))     # -1 - получаем очередной элемент генератора

Особенности выражений-генераторов

  1. Генаратор нельзя писать без скобок — это синтаксическая ошибка.
    # my_gen = i for i in list_a      # SyntaxError: invalid syntax

  2. При передаче в функцию дополнительные скобки необязательны
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_sum = sum(i for i in list_a)
    # my_sum = sum((i for i in list_a))  # так тоже можно
    print(my_sum)   # 12

  3. Нельзя получить длину функцией len()
    # my_len = len(i for i in list_a)  # TypeError: object of type 'generator' has no len()

  4. Нельзя распечатать элементы функцией print()
    print(my_gen)   # <generator object <genexpr> at 0x7f162db32af0>
    

  5. Обратите внимание, что после прохождения по выражению-генератору оно остается пустым!
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_gen = (i for i in list_a)
    print(sum(my_gen))  # 12
    print(sum(my_gen))  # 0

  6. Выражение-генератор может быть бесконечным.
    import itertools
    inf_gen = (x for x in itertools.count())  # бесконечный генератор от 0 to бесконечности!

    Будьте осторожны в работе с такими генераторами, так как при не правильном использовании «эффект» будет как от бесконечного цикла.

  7. К выражению-генератору не применимы срезы!
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_gen = (i for i in list_a)
    my_gen_sliced = my_gen[1:3]
    # TypeError: 'generator' object is not subscriptable

  8. Из генератора легко получать нужную коллекцию. Это подробно рассматривается в следующей главе.

5. Генерация стандартных коллекций

5.1 Создание коллекций из выражения-генератора

Создание коллекций из выражения-генератора с помощью функций list(), tuple(), set(), frozenset()

Примечание: Так можно создать и неизменное множество и кортеж, так как неизменными они станет уже после генерации.

Внимание: Для строки такой способ не работает! Синтаксис создания генератора словаря таким образом имеет свои особенности, он рассмотрен в следующем под-разделе.

  1. Передачей готового выражения-генератора присвоенного переменной в функцию создания коллекции.
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_gen = (i for i in list_a)   # выражение-генератор
    my_list = list(my_gen) 
    print(my_list)          # [-2, -1, 0, 1, 2, 3, 4, 5]

  2. Написание выражения-генератора сразу внутри скобок вызываемой функции создания коллекции.
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_list = list(i for i in list_a)
    print(my_list)          # [-2, -1, 0, 1, 2, 3, 4, 5]

    То же самое для кортежа, множества и неизменного множества

    # кортеж
    my_tuple = tuple(i for i in list_a)
    print(my_tuple)         # (-2, -1, 0, 1, 2, 3, 4, 5)
    
    # множество
    my_set = set(i for i in list_a)
    print(my_set)           # {0, 1, 2, 3, 4, 5, -1, -2}
    
    # неизменное множество
    my_frozenset = frozenset(i for i in list_a)
    print(my_frozenset)     # frozenset({0, 1, 2, 3, 4, 5, -1, -2})

5.2 Специальный синтаксис генераторов коллекций

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

Соответственно, вместо особенности выражений-генераторов перечисленных выше, такая коллекция будет обладать всеми стандартными свойствами характерными для коллекции данного типа.

Обратите внимание, что для генерации множества и словаря используются одинаковые скобки, разница в том, что у словаря указывается двойной элемент ключ: значение.

  1. Генератор списка (list comprehension)
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_list = [i for i in list_a]
    print(my_list)          # [-2, -1, 0, 1, 2, 3, 4, 5]


    Не пишите круглые скобки в квадратных!

    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_list = [(i for i in list_a)]
    print(my_list)          # [<generator object <genexpr> at 0x7fb81103bf68>]

  2. Генератор множества (set comprehension)
    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    my_set= {i for i in list_a}
    print(my_set)       # {0, 1, 2, 3, 4, 5, -1, -2} - порядок случаен

  3. Генератор словаря (dictionary comprehension)

    переворачивание словаря

    dict_abc = {'a': 1, 'b': 2, 'c': 3, 'd': 3}
    dict_123 = {v: k for k, v in dict_abc.items()}
    print(dict_123)  # {1: 'a', 2: 'b', 3: 'd'}
                     # Обратите внимание, мы потеряли "с"! Так как значения были одинаковы, 
                     # то когда они стали ключами, только последнее значение сохранилось.


    Словарь из списка:

    list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
    dict_a = {x: x**2 for x in list_a}
    print(dict_a)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, -2: 4, -1: 1, 5: 25}

    Важно! Такой синтаксис создания словаря работает только в фигурных скобках, выражение-генератор так создать нельзя, для этого используется немного другой синтаксис (благодарю longclaps за подсказку в комментариях):

    # dict_gen = (x: x**2 for x in list_a)      # SyntaxError: invalid syntax
    dict_gen = ((x, x ** 2) for x in list_a)    # Корректный вариант генератора-выражения для словаря
    # dict_a = dict(x: x**2 for x in list_a)    # SyntaxError: invalid syntax
    dict_a = dict((x, x ** 2) for x in list_a)  # Корректный вариант синтаксиса от @longclaps

5.3 Генерация строк

Для создания строки вместо синтаксиса выражений-генераторов используется метод строки . join(), которому в качестве аргументов можно передать выражение генератор.
Обратите внимание: элементы коллекции для объединения в строку должны быть строками!

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
# используем генератор прямо в .join() одновременно приводя элементы к строковому типу
my_str = ''.join(str(x) for x in list_a)
print(my_str)  # -2-1012345

6. Периодичность и частичный перебор

6.1 Работа с enumerate()

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

Для подобных задач можно использовать функцию enumerate(), задающую счетчик при обходе итератора в цикле:

for i, x in enumerate(iterable)

здесь x — текущий элемент i — его порядковый номер, начиная с нуля

Проиллюстрируем работу с индексами:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_d = [(i, x) for i, x in enumerate(list_a)]
print(list_d)   # [(0, -2), (1, -1), (2, 0), (3, 1), (4, 2), (5, 3), (6, 4), (7, 5)]

Теперь попробуем решить реальную задачу — выберем в генераторе списка каждый третий элемент из исходного списка:

list_a = [-2, -1, 0, 1, 2, 3, 4, 5]
list_e = [x for i, x in enumerate(list_a, 1) if i % 3 == 0]
print(list_e)   # [0, 3]

Важные особенности работы функции enumerate():

  1. Возможны два варианта вызова функции enumerate():
    • enumerate(iterator) без второго параметра считает с 0.
    • enumerate(iterator, start) — начинает считать с значения start. Удобно, например, если нам надо считать с 1, а не 0.

  2. enumerate() возвращает кортеж из порядкового номера и значения текущего элемента итератора. Кортеж в выражении-генераторе результате можно получить двумя способами:
    • (i, j) for i, j in enumerate(iterator) — скобки в первой паре нужны!
    • pair for pair in enumerate(mylist) — мы работаем сразу с парой

  3. Индексы считаются для всех обработанных элементов, без учета прошли они в дальнейшем условие или нет!
    first_ten_even = [(i, x) for i, x in enumerate(range(10)) if x % 2 == 0]
    print(first_ten_even)   # [(0, 0), (2, 2), (4, 4), (6, 6), (8, 8)]

  4. Функция enumerate() не обращается к каким-то внутренним атрибутам коллекции, а просто реализует счетчик обработанных элементов, поэтому ничего не мешает ее использовать для неупорядоченных коллекций не имеющих индексации.
  5. Если мы ограничиваем количество элементов включенных в результат по enumerate() счетчику (например if i < 10), то итератор будет все равно обработан целиком, что в случае огромной коллекции будет очень ресурс-затратно. Решение этой проблемы рассматривается ниже в под-разделе «Перебор части итерируемого».

6.2 Перебор части итерируемого.

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

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

Выходом может быть использование функции islice() из пакета itertools.

import itertools
first_ten = (itertools.islice((x for x in range(1000000000) if x % 2 == 0), 10))
print(list(first_ten))  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Для сомневающихся: проверяем время выполнения

import time
import itertools

# На генераторе с малым количеством элементов
start_time = time.time()
first_ten = (itertools.islice((x for x in range(100) if x % 2 == 0), 10))
print(list(first_ten))  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
elapsed_time = time.time() - start_time
print(elapsed_time)  # 3.409385681152344e-05

# На генераторе с огромным количеством элементов
start_time = time.time()
first_ten = (itertools.islice((x for x in range(100000000) if x % 2 == 0), 10))
print(list(first_ten))  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
elapsed_time = time.time() - start_time
print(elapsed_time)  # 1.1205673217773438e-05

# То есть максимальное количество элементов в генераторе range() мы увеличили на 6 порядков, 
# а время исполнения осталось того же порядка

7. Вложенные циклы и генераторы

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

7.1 Вложенные циклы

В результате генерации получаем одномерную структуру.

Важно! При работае с вложенными циклами внутри генератора выражений порядок следования инструкций for in будет такой же (слева-направо), как и в аналогичном решении без генератора, только на циклах (сверху-вниз)! Тоже справедливо и при более глубоких уровнях вложенности.

7.1.1 Вложенные циклы for где циклы идут по независимым итераторам

Общий синтаксис: [expression for x in iter1 for y in iter2]
Применение: генерируем одномерную структуру, используя данные из двух итераторов.

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

rows = 1, 2, 3
cols = 'a', 'b'
my_dict = {(col, row): 0 for row in rows for col in cols}
print(my_dict)  # {('a', 1): 0, ('b', 2): 0, ('b', 3): 0, ('b', 1): 0, ('a', 3): 0, ('a', 2): 0}

Дальше можем задавать новые значения или получать их

my_dict['b', 2] = 10   # задаем значение по координатному ключу-кортежу
print(my_dict['b', 2])   # 10 - получаем значение по координатному ключу-кортежу

Тоже можно сделать и с дополнительными условиями-фильтрами в каждом цикле:

rows = 1, 2, 3, -4, -5
cols = 'a', 'b', 'abc'
# Для наглядности разнесем на несколько строк
my_dict = {
    (col, row): 0  # каждый элемент состоит из ключа-кортежа и нулевого знаечния
    for row in rows if row > 0   # Только положительные значения
    for col in cols if len(col) == 1  # Только односимвольные
    }
print(my_dict)  # {('a', 1): 0, ('b', 2): 0, ('b', 3): 0, ('b', 1): 0, ('a', 3): 0, ('a', 2): 0}

Эта же задача решенная с помощью цикла

rows = 1, 2, 3, -4, -5
cols = 'a', 'b', 'abc'
my_dict = {}
for row in rows:
    if row > 0:
        for col in cols:
            if len(col) == 1:
                my_dict[col, row] = 0
print(my_dict)  # {('a', 1): 0, ('b', 2): 0, ('b', 3): 0, ('b', 1): 0, ('a', 3): 0, ('a', 2): 0}

7.

2))
# и потому совсем не рекомендуется к использованию для таких целей

7.2 Вложенные генераторы

Вложенными могут быть не только циклы for внутри выражения-генератора, но и сами генераторы.
Такой подход применяется когда нам надо строить двумерную структуру.

Важно!: В отличии от примеров выше с вложенными циклами, для вложенных генераторов, вначале обрабатывается внешний генератор, потом внутренний, то есть порядок идет справа-налево.

Ниже рассмотрим два варианта подобного использования.

7.2.1 — Вложенный генератор внутри генератора — двумерная из двух одномерных

Общий синтаксис: [[expression for y in iter2] for x in iter1]
Применение: генерируем двумерную структуру, используя данные из двух одномерных итераторов.

Для примера создадим матрицу из 5 столбцов и 3 строк и заполним ее нулями:

w, h = 5, 3  # зададим ширину и высотку матрицы
matrix = [[0 for x in range(w)] for y in range(h)]
print(matrix)   # [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Создание этой же матрицы двумя вложенными циклами — обратите внимание на порядок вложения

matrix = []
for y in range(h):
    new_row = []
    for x in range(w):
        new_row.append(0)
    matrix.append(new_row)
print(matrix)   # [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Примечание: После создания можем работать с матрицей как с обычным двумерным массивом

# теперь можно добавлять значения по координатам (координаты - индексы в списке списков)
matrix[0][0] = 1
matrix[1][3] = 3
print(matrix)   # [[1, 0, 0, 0, 0], [0, 0, 0, 3, 0], [0, 0, 0, 0, 0]]

# Получаем значение по произвольным координатам
x, y = 1, 3
print(matrix[x][y])  # 3

7.2.2 — Вложенный генератор внутри генератора — двумерная из двумерной

Общий синтаксис: [[expression for y in x] for x in iterator]
Применение: Обходим двумерную структуру данных, сохраняя результат в другую двумерную структуру.

Возьмем матрицу:

matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

Возведем каждый элемент матрицы в квадрат:

squared = [[cell**2 for cell in row] for row in matrix]
print(squared)    # [[1, 4, 9, 16], [25, 36, 49, 64], [81, 100, 121, 144]]

Эта же операция в виде вложенных циклов

squared = []
for row in matrix:
    new_row = []
    for cell in row:
        new_row.append(cell**2)
    squared.append(new_row)
print(squared)    # [[1, 4, 9, 16], [25, 36, 49, 64], [81, 100, 121, 144]]

Обобщим все вышеперечисленные варианты в одной схеме (полный размер по клику):

7.3 — Генератор итерирующийся по генератору

Так как любой генератор может использоваться как итератор в цикле for, это так же можно использовать и для создания генератора по генератору.
При этом синтаксически это может записываться в два выражения или объединяться во вложенный генератор.

Проиллюстрирую и такую возможность.

Допустим у нас есть два таких генератора списков:

list_a = [x for x in range(-2, 4)]    # Так сделано для дальнейшего примера синтаксиса, 
                                      # конечно в подобной задаче досточно только range(-2, 4)
list_b = [x**2 for x in list_a]

Тоже самое можно записать и в одно выражение, подставив вместо list_a его генератор списка:

list_c = [x**2 for x in [x for x in range(-2, 4)]]
print(list_c)  # [4, 1, 0, 1, 4, 9]

UPD от longclaps: Преимущество от комбинирования генераторов на примере сложной функции f(x) = u(v(x))

list_c = [t + t ** 2  for t in (x ** 3 + x ** 4 for x in range(-2, 4))]

8. Использование range()

Говоря о способах генерации коллекций, нельзя обойти вниманием простую и очень удобную функцию range(), которая предназначена для создания арифметических последовательностей.

Особенности функции range():

  • Наиболее часто функция range() применяется для запуска цикла for нужное количество раз. Например, смотрите генерацию матрицы в примерах выше.
  • В Python 3 range() возвращает генератор, который при каждом к нему обращении выдает очередной элемент.
  • Исполльзуемые параметры аналогичны таковым в срезах (кроме первого примера с одним параметром):
    • range(stop) — в данном случае с 0 до stop-1;
    • range(start, stop) — Аналогично примеру выше, но можно задать начало отличное от нуля, можно и отрицательное;
    • range(start, stop, step) — Добавляем параметр шага, который может быть отрицательным, тогда перебор в обратном порядке.

  • В Python 2 были 2 функции:
    • range(…) которая аналогична выражению list(range(…)) в Python 3 — то есть она выдавала не итератор, а сразу готовый список. То есть все проблемы возможной нехватки памяти, описанные в разделе 4 актуальны, и использовать ее в Python 2 надо очень аккуратно!
    • xrange(…) — которая работала аналогично range(…) в Python 3 и из 3 версии была исключена.

Примеры использования:

print(list(range(5)))           # [0, 1, 2, 3, 4]
print(list(range(-2, 5)))       # [-2, -1, 0, 1, 2, 3, 4]
print(list(range(5, -2, -2)))   # [5, 3, 1, -1]

9. Приложение 1. Дополнительные примеры

9.1 Последовательный проход по нескольким спискам
import itertools
l1 = [1,2,3]
l2 = [10,20,30]
result = [l*2 for l in itertools.chain(l1, l2)]
print(result)   # [2, 4, 6, 20, 40, 60]

9.2 Транспозиция матрицы

(Преобразование матрицы, когда строки меняются местами со столбцами).

Возьмем матрицу.

matrix = [[1, 2, 3, 4],
          [5, 6, 7, 8],
          [9, 10, 11, 12]]

Сделаем ее транспозицию с помощью генератора выражений:

transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed)  # [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Эта же транспозиция матрицы в виде цикла

transposed = []
for i in range(len(matrix[0])):
    new_row = []
    for row in matrix:
        new_row. append(row[i])
    transposed.append(new_row)
print(transposed)  # [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

И немного черной магии от @longclaps

transposed = list(map(list, zip(*matrix)))
print(transposed)  # [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

9.3 Задача выбора только рабочих дней
# Формируем список дней от 1 до 31 с которым будем работать
days = [d for d in range(1, 32)]

# Делим список дней на недели
weeks = [days[i:i+7] for i in range(0, len(days), 7)]
print(weeks)   # [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21], [22, 23, 24, 25, 26, 27, 28], [29, 30, 31]]

# Выбираем в каждой неделе только первые 5 рабочих дней, отбрасывая остальные
work_weeks = [week[0:5] for week in weeks]
print(work_weeks)   # [[1, 2, 3, 4, 5], [8, 9, 10, 11, 12], [15, 16, 17, 18, 19], [22, 23, 24, 25, 26], [29, 30, 31]]

# Если нужно одним списком дней - можно объединить
wdays = [item for sublist in work_weeks for item in sublist]
print(wdays)   # [1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 29, 30, 31]

Можно убрать выходные еще более изящно, используя только индексы

# Формируем список дней от 1 до 31 с которым будем работать
days = [d for d in range(1, 32)]

wdays6 = [wd for (i, wd) in enumerate(days, 1) if i % 7 != 0]  # Удаляем каждый 7-й день
# Удаляем каждый 6 день в оставшихся после первого удаления:
wdays5 = [wd for (i, wd) in enumerate(wdays6, 1) if i % 6 != 0]

print(wdays5)
# [1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 29, 30, 31]

# Обратите внимание, что просто объединить два условия в одном if не получится,
# как минимум потому, что 12-й день делится на 6, но не выпадает на последний 2 дня недели!

# Шикарное короткое решение от @sophist:
days = [d + 1 for d in range(31) if d % 7 < 5]

10. Приложение 2. Ссылки по теме

  1. Хорошая англоязычная статья с детальным объяснением что такое генераторы и итераторыИллюстрация из статьи:
  2. Если у Вас есть сложности с пониманием логики работы с генераторными выражениями, посмотрите интересную англоязычную статью, где проводятся аналогии между генераторными выражениями и работой с SQL и таблицами Excel. Например так:
    squared_evens = [n ** 2              # SELECT
                    for n in numbers    # FROM
                    if n % 2 == 0]      # WHERE

  3. UPD от fireSparrow: Существуюет расширение Python — PythonQL, позволяющее работать с базами данных в стиле генераторов коллекций.
  4. Иллюстрированная статья на английском, довольно наглядно показывает синтаксис генераторных выражений.
  5. Если требуются дополнительные примеры по теме вложенных генераторных выражений (статья на английском).
Приглашаю к обсуждению:

  • Если я где-то допустил неточность или не учёл что-то важное — пишите в комментариях, важные комментарии будут позже добавлены в статью с указанием вашего авторства.
  • Если какие-то моменты не понятны и требуется уточнение — пишите ваши вопросы в комментариях — или я или другие читатели дадут ответ, а дельные вопросы с ответами будут позже добавлены в статью.

Сделайте простой генератор словаря Python

Вот пример кода, в основном выполняющего итерацию.

>>> d = {'lol': {'12': '3',  '35':''}, 'w': {'12': '3', '35':''}}

>>> def iteritems(s):
...     keys = s.keys()
...     for key in keys:
...             yield key, s[key]
... 
>>> for k, v in iteritems(d):
...     print k, v
... 
w {'12': '3', '35': ''}
lol {'12': '3', '35': ''}

Это действительный генератор. Но генератор iteritems должен вызвать s.keys() , чтобы кэшировать все ключи в словаре. Если словарь большой (более 100 элементов), это не очень эффективно для памяти.

Но поскольку словарь не является упорядоченной структурой, я предполагаю, что получение этого списка ключей является обязательным.

Можно возразить: количество ключей намного меньше, чем количество элементов, подсчитывающих ключи и значения.

Есть предложения? Или лучше (и, конечно, мне нужно поддерживать вложенную итерацию, но это совсем другое дело).

python

dictionary

generator

Поделиться

Источник


CppLearner    

04 сентября 2012 в 19:56

2 ответа


  • Python генератор словаря одиночных операторов

    Есть ли способ в Python сделать это new_list = [x for x in items] (понимание списка) для словаря? Что-то вроде new_dict = [x=>y for x, y in items.iteritems()] так что я получаю {x: y}

  • Первичный генератор в python

    Я пишу простой генератор, который отличается от любого другого в этой ссылке генератор в Python генерация простых чисел Вот мой код def sequence(): i = 1 while True: i += 2 yield i def prime_generator(n): i = 2 it = sequence() while i < n: it= filter(lambda x: x % i, it) i = next(it) yield i…



11

Используйте .iterkeys() в python 2.x; в python 3.x .keys() — это представление , а не новый список. В python 2.7 вы также можете использовать метод viewkeys() , чтобы получить то же самое.

Существует эквивалентный метод .iteritems() , делающий ваш служебный метод избыточным. 🙂 Те же замечания применимы здесь для python 3.x и 2.7, см. прилагаемую документацию.

Поделиться


Martijn Pieters    

04 сентября 2012 в 19:57



3

Вы изобретаете велосипед заново, чтобы избежать создания промежуточного списка в Python 2.x просто используйте:

for k, v in d.iteritems():
    print k, v

В Python 3.x использовать:

for k, v in d.items():
    print(k, v)

потому что метод .items() в этой версии возвращает итеративный объект представления .

Поделиться


martineau    

05 сентября 2012 в 01:42


Похожие вопросы:

Простой генератор простых чисел в Python

Может ли кто-нибудь сказать мне, что я делаю не так с этим кодом? В любом случае это просто печать ‘count’. Мне просто нужен очень простой генератор простых чисел (ничего особенного). import math…

нужен генератор случайных слов с помощью словаря

Я делаю игру палача, поэтому мне нужен генератор случайных слов. Моя цель состоит в том, чтобы пользователь сказал, сколько букв он хочет в слове, и вывел случайное слово с таким количеством букв. Я…

Python изменить генератор

Я реализовал (простой) генератор python. Теперь я хочу сделать еще один (из него), который будет перебирать все значения, но последний. def gen(x): # Generate the interval [x, 10] if x <= 10:…

Python генератор словаря одиночных операторов

Есть ли способ в Python сделать это new_list = [x for x in items] (понимание списка) для словаря? Что-то вроде new_dict = [x=>y for x, y in items.iteritems()] так что я получаю {x: y}

Первичный генератор в python

Я пишу простой генератор, который отличается от любого другого в этой ссылке генератор в Python генерация простых чисел Вот мой код def sequence(): i = 1 while True: i += 2 yield i def…

Напишите генератор в пользовательский класс словаря в Swift с помощью дженериков

SWIFT / IOS / Словарь / Генератор / Последовательность Я создал упорядоченный словарь с помощью blogpost http://timekl.com/blog/2014/06/02/learning-swift-ordered-dictionaries / Как описано в блоге,…

генератор паролей с использованием Python

я получил программу от одного из сайтов, чтобы сделать генератор паролей. import string from random import * characters = string.ascii_letters + string.punctuation + string.digits password =…

Разбиение словаря на несколько копий в python

У меня есть словарь python d = { ‘facets’:{‘style’:collared,’pocket’:yes}, ‘vars’:[ {‘facets’:{‘color’:blue, ‘size’:XL}}, {‘facets’:{‘color’:blue, ‘size’:L}} ] } Поскольку в ключе ‘vars’ есть 2…

Как поддерживать порядок вложенного словаря в python (не простой, а вложенный)?

Я просто скопировал словарь, сгенерированный из другой программы, в программу python(жестко закодированную в коде python). a = {‘b’:{‘d’:3, ‘c’:4}, ‘a’:2} но во время чтения словаря порядок…

Python простой генератор звуковых тонов

Ищу какой-нибудь (простой) генератор тонов python для использования в следующем скрипте, работающем на raspi с звуковой картой USB. На лету требуется включение/выключение тона и изменение частоты….

python — Как сделать генератор словаря?

Сделал совершенно иначе.

import json
import urllib
import os
import copy
import ast
import operator
import requests


DISTANCE_MATRIX_URL = "http://maps.googleapis.com/maps/api/distancematrix/"


class DM(object):

    def __init__(self, api_key=None, url=DISTANCE_MATRIX_URL):

        self.api_key = api_key
        self.url = url
        self.response = ''
        self.dict_response = {'distance': {'value': {}, 'text': {}, },
                              'duration': {'value': {}, 'text': {}, },
                              }

        self.origins = ''
        self.destinations = ''

    def make_request(self, origins, destinations, mode='driving'):
        data = {}
        self.origins = [origins] if type(origins) == str else origins
        self.destinations = [destinations] if type(destinations) == str else destinations
        data['origins'] = origins if type(origins) == str else '|'.join(origins)
        data['destinations'] = destinations if type(destinations) == str else '|'.join(destinations)
        data['mode'] = mode

        url_values = urllib.parse.urlencode(data)
        output_format = 'json'
        url = os.path.join(self.url, output_format)

        self.response = ast.literal_eval((urllib.request.urlopen(url + '?' + url_values).read()).decode("utf-8"))['rows']
        self.dict_response = {'distance': {'value': {}, 'text': {}, },  # Reset temporary dict
                              'duration': {'value': {}, 'text': {}, },
                              }


    def __get_response_element_data(self, key1, key2):
        if not self. dict_response[key1][key2]:
            l = self.response
            for i, orig in enumerate(self.origins):
                self.dict_response[key1][key2][orig] = {}
                for j, dest in enumerate(self.destinations):
                    if l[i]['elements'][j]['status'] == 'OK':
                        self.dict_response[key1][key2][orig][dest] = l[i]['elements'][j][key1][key2]
                    else:
                        self.dict_response[key1][key2][orig][dest] = l[i]['elements'][j]['status']

        return self.dict_response[key1][key2]

    def get_distance_values(self):
        return self.__get_response_element_data('distance', 'value')

    def get_distance_texts(self):
        return self.__get_response_element_data('distance', 'text')

    def get_time_values(self):
        return self.__get_response_element_data('duration', 'value')

    def get_time_texts(self):
        return self.__get_response_element_data('duration', 'text')

    def get_closest_points(self, max_distance=None, num=10, origin_index=0, origin_raw=None):
        if not self.dict_response['distance']['value']:
            self.get_distance_values()

        if origin_raw:
            origin = copy.deepcopy(self.dict_response['distance']['value'][origin_raw])
        else:
            origin = copy.deepcopy(self.dict_response['distance']['value'][self.origins[origin_index]])

        tmp_origin = copy.deepcopy(origin)
        if max_distance:
            for k, v in tmp_origin.iteritems():
                if v > max_distance:
                    del(origin[k])

        if origin:
            return sorted(origin.iteritems(), key=operator.itemgetter(1))[:num]

Работает это так:

a = DM()

a.make_request(['Minsk'], ['Moscow'])

# Отдаём один origin (точка, от которой считаем) и один destination (пункт назначения).

Если хотим получить результат в текстовом формате (он менее точный), то делаем так:

a. get_distance_texts()

и получаем результат:

{'Minsk': {'Moscow': '717 km'}}

Если же, нужно посчитать расстояние между одним origin и несколькими destination, то делаем так:

a.make_request(['Minsk'], ['Moscow', 'Sankt-Peterburg'])
a.get_distance_texts()
{'Minsk': {'Moscow': '717 km', 'Sankt-Peterburg': '795 km'}}

В действительности, расстояние иное. Но разница между Москвой и СПб более-менее точная.

Вот так получаем данные в метрах:

a.get_distance_values()
{'Minsk': {'Moscow': 717038, 'Sankt-Peterburg': 795481}}

А вот так, получаем duration

a.get_time_values()
{'Minsk': {'Moscow': 28751, 'Sankt-Peterburg': 36415}}

Точно так же, с текстовым отображением:

a.get_time_texts()
{'Minsk': {'Moscow': '7 hours 59 mins', 'Sankt-Peterburg': '10 hours 7 mins'}}

Если же надо узнать про расстояние между несколькоими origins и destinations, то так:

a.make_request(['Minsk', 'Moscow'], ['Moscow', 'Varshava'])
a.get_distance_texts()
{'Minsk': {'Moscow': '717 km', 'Varshava': '554 km'}, 'Moscow': {'Moscow': '1 m', 'Varshava': '1,264 km'}}

А вот так:

a.get_closest_points()

Получаем список из кортежей, где первый элемент — пункт назначения, а второй — расстояние от первого origin

[('Varshava', 553521), ('Moscow', 717038)]

Покоряем Python — уроки для начинающих: Понимание генераторов множеств и словарей

Сейчас 2 часа  ночи, я ничего не понимаю, что относится к программированию на Python, но пишу этот пост… надеюсь кому то он поможет.

>>> {x * x for x in range(10)}         # Генератор
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> set(x * x for x in range(10)) # Генератор и конструктор типа
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> {x: x * x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

>>> dict((x, x * x) for x in range(10))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Мы всегда можем конструировать объекты результата вручную.

Ниже приведется реализация, эквивалентна двум последним генераторам:

>>> res = set()
>>> for x in range(10):  # Эквивалент генератора множеств
…     res.add(x * x)

>>> res
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}
————————————-
>>> res = {}

>>> for x in range(10):  # Эквивалент генератора словарей
…     res[x] = x * x

>>> res
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Если вам потребуется воспроизводить ключи и значения по запросу, 
используйте выражение-генератор:

>>> G = ((x, x * x) for x in range(10))

>>> next(G)

(0, 0)

>>> next(G)

(1, 1)

Расширенный синтаксис генераторов множеств и словарей

Как  и  генераторы  списков,  генераторы  множеств и  словарей  поддерживают
инструкции if, позволяющие  отфильтровывать элемен

ты, – следующие конструкции воспроизводят квадраты четных 

чисел (то есть чисел, которые делятся на 2 без остатка) в заданном диапазоне:

>>> [x * x for x in range(10) if x % 2 == 0] # Списки упорядочены
[0, 4, 16, 36, 64]

>>> {x * x for x in range(10) if x % 2 == 0} # А множества — нет
{0, 16, 4, 64, 36}

>>> {x: x * x for x in range(10) if x % 2 == 0}# Как и ключи
                                               # словаря
{0: 0, 8: 64, 2: 4, 4: 16, 6: 36}

Вложенные циклы for также являются допустимыми, хотя неупорядоченные типы данных, не допускают появления дубликатов, может несколько осложнять интерпретацию результатов:

>>> [x + y for x in [1, 2, 3] for y in [4, 5, 6]] # Списки сохраняют дубликаты

[5, 6, 7, 6, 7, 8, 7, 8, 9]

>>> {x + y for x in [1, 2, 3] for y in [4, 5, 6]} # А множества — нет

{8, 9, 5, 6, 7}

>>> {x: y for x in [1, 2, 3] for y in [4, 5, 6]} # Как и ключи словарей

{1: 6, 2: 6, 3: 6}

Подобно генераторам списков, генераторы множеств и словарей способны выполнять итерации по итераторам любых типов – спискам, строкам, файлам,диапазонам целых чисел и любым другим объектам, поддерживающим протокол итераций:
>>> {x + y for x in ‘ab’ for y in ‘cd’}
{‘bd’, ‘ac’, ‘ad’, ‘bc’}

>>> {x + y: (ord(x), ord(y)) for x in ‘ab’ for y in ‘cd’}
{‘bd’: (98, 100), ‘ac’: (97, 99), ‘ad’: (97, 100), ‘bc’: (98, 99)}

>>> {k * 2 for k in [‘spam’, ‘ham’, ‘sausage’] if k[0] == ‘s’}

{‘sausagesausage’, ‘spamspam’}

>>> {k. upper(): k * 2 for k in [‘spam’, ‘ham’, ‘sausage’] if k[0] == ‘s’}

{‘SAUSAGE’: ‘sausagesausage’, ‘SPAM’: ‘spamspam’}







Немного ключевых слов, если ты не бот для индексации тогда читать не обязательно!!!

Программирование, программирование на Python, Python, Python3.0, Python3, Python3.x, Python3.1, Python3.2, Python3.3, Python 3.x, Python, Python 3.0, Python 3, Python 3.1, Python 3.2, Python 3.3, Python 3.3.1

Python. Работа со словарями. Обход. Генерирование. Примеры

Работа со словарями. Встроенные функции list(), zip(). Обход словаря. Генераторы словарей. Сортировка. Сочетание словарей с множествами


Содержание


Поиск на других ресурсах:

1. Встроенная функция list(). Пример применения со словарями

Использование встроенной функции list() позволяет получать списки значений, ключей и пар (ключ:значение). Чтобы сформировать соответствующий список с помощью list() нужно использовать один из следующих методов:

  • метод values() позволяет получить список значений словаря;
  • метод keys() позволяет получить список ключей словаря;
  • метод items() позволяет получить список пар (ключ:значение).

Более подробно об использовании методов values(), keys(), items() описывается здесь.

Пример.

# Работа со словарями
# Встроенный метод list()

# Исходный словарь
Figures = { 1:'Rectangle', 2:'Triangle', 3:'Circle'}

# 1. Получить список значений словаря
values = list(Figures.values())
print(values) # ['Rectangle', 'Triangle', 'Circle']

# 2. Получить список ключей словаря
keys = list(Figures.keys()) # keys = [1, 2, 3, 4]
print(keys)

# 3. Получить список пар (ключ:значение)
pairs = list(Figures.items())
print(pairs) # [(1,'Rectangle'),(2,'Triangle'),(3,'Circle')]

  ⇑

2.

Встроенная функция zip(). Пример создания словаря

Функция zip() позволяет создать словарь путем объединения списков ключей и значений.

Пример.

# Словари. Функция zip()

# Создание словаря из списков ключей и значений
Numbers = dict(zip([1, 2, 3], ['One', 'Two', 'Three']))
print(Numbers) # {1: 'One', 2: 'Two', 3: 'Three'}

  ⇑

3. Обход словаря с помощью цикла for. Пример

В примере демонстрируется обход словаря с помощью цикла for и вывод всех пар (ключ:значение).

Пример.

# Работа со словарями
# Обход словаря с помощью цикла for

# Исходный словарь
Months = { 1:'Jan', 2:'Feb', 3:'Mar',
           4:'Apr', 5:'May', 6:'Jun',
           7:'Jul', 8:'Aug', 9:'Sep',
           10:'Oct', 11:'Nov', 12:'Dec' }

# Цикл for обхода словаря
# в цикле mn - ключ, Months[mn] - значение
for mn in Months:
    print(mn, ': ', Months[mn])

Результат выполнения программы

1 : Jan
2 : Feb
3 : Mar
4 : Apr
5 : May
6 : Jun
7 : Jul
8 : Aug
9 : Sep
10 : Oct
11 : Nov
12 : Dec


  ⇑

4. Использование словарей для разреженных структур данных. Пример

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

Пример. В примере продемонстрировано представление разреженной матрицы чисел в виде словаря. Пусть задана матрица

,

которую нужно представить в виде словаря. Решение задачи следующее:

# Словари. Представление разреженных структур данных

# Матрица M
M = {} # создать пустой словарь
M[(0, 1)] = 1 # ключ=(0,1): значение=1
M[(1, 2)] = 3 # пара (ключ:значение) = ((1,2):3)
M[(2, 0)] = 2 # пара (ключ:значение) = ((2,0):2)

print(M) # {(0, 1): 1, (1, 2): 3, (2, 0): 2}

В вышеприведенном примере ключами словаря являются кортежи, которые содержат координаты непустых элементов. Так, значение ключа (2, 0) означает строку 2, столбец 0.

  ⇑

5. Создание словарей с помощью генераторов словарей. Примеры

Словари можно создавать с использованием генераторов словарей. В этом случае используется один из нижеследующих способов:

  • сгенерировать словарь на основе заданного выражения;
  • сгенерировать словарь на основе итерированного объекта;
  • инициализировать словарь из списков ключей;
  • сгенерировать словарь с использованием функции zip().

В примере демонстрируется использование всех способов.

Пример.

# Словари. Генераторы словарей

# 1. Сгенерировать словарь на основе заданного выражения
# Генерируются пары (ключ: значение) = (i: 5*i)
A = { i: i*5 for i in [10,20,30,40] }
print(A) # {10: 50, 20: 100, 30: 150, 40: 200}

# 2. Сгенерировать словарь по итерируемому объекту (строке)
s = 'Hello'
B = { sym: sym*3 for sym in s } # пара (sym:sym*3)
print(B) # {'H': 'HHH', 'e': 'eee', 'l': 'lll', 'o': 'ooo'}

numbers = [ 15, 25, 30 ]
C = { num: str(num) for num in numbers }
print(C) # {15: '15', 25: '25', 30: '30'}

# 3. Инициализация словаря из списка ключей
# Заполнить список ключей значением, введенным с клавиатуры
value = int(input('Input value: '))
listKeys = [ 1, 2, 3, 4 ]
D = { lk:value for lk in listKeys }
print(D)

# Заполнить список ключей значением None
value = None
listKeys = [ 'a', 'b', 'c' ]
E = { lk:value for lk in listKeys }
print(E) # {'a': None, 'b': None, 'c': None}

# 4. Сгенерировать словарь с использованием функции zip()
WMonths = [ 'Dec', 'Jan', 'Feb']
NumMonths = [ 12, 1, 2 ]
F = { nm:wm for (nm,wm) in zip(NumMonths,WMonths) }
print(F) # {12: 'Dec', 1: 'Jan', 2: 'Feb'}

  ⇑

6. Сортировка словаря. Пример

Как известно, ключи в словаре сохраняются в произвольном порядке. .

# Словари. Объединение словарей и множеств

# Исходный словарь
A = { 1:'One', 2:'Two', 3:'Three', 4:'Four', 5:'Five' }

# Объединение отображения ключей и множеств,
# получается новое множество

# 1. Получить отображение ключей
keys = A.keys() # keys = dict_keys([1, 2, 3, 4, 5])

# 2. Сформировать новое множество ключей которое содержит число 2
B = keys & {2} # B = {2} - новое множество

# 3. Сформировать новое множество ключей, которое содержит числа 3, 5
C = keys {3, 5} # {1, 2, 4}

# 4. Добавить к множеству ключей числа 8, 11
D = keys | { 8, 11 } # D = {1, 2, 3, 4, 5, 8, 11}

  ⇑


Связанные темы

  ⇑


 

Генераторы | Python

В Python просто генераторы и генераторы списков — разные вещи. Здесь есть проблема перевода с английского. То, что мы привыкли называть генератором списка, в английском варианте звучит как «list comprehension» и к генераторам никакого отношения не имеет.

Слово «comprehension» (понимание, осмысление) оказывается как бы не в тему при переводе на русский. Получается что-то вроде «понимание списка». Поэтому мы говорим «генератор списка», понимая под словом «генератор» не объект, а синтаксическую конструкцию, которая генерирует, то есть создает, список.

С другой стороны, объекты-генераторы — это особые объекты-функции, которые между вызовами сохраняют свое состояние. В цикле for они ведут себя подобно итерируемым объектам, к которым относятся списки, словари, строки и др. Однако генераторы поддерживают метод __next__(), а значит являются разновидностью итераторов. 

Быстрым способом создания относительно простых объектов-генераторов являются генераторные выражения — generator expressions. Синтаксис этих выражений похож на синтаксис генераторов списков. Однако они возвращают разные типы объектов. Первый — объект-генератор. Второй — список.

Сначала рассмотрим генераторы списков, чтобы привыкнуть к синтаксической конструкции.

Генераторы списков

В Python генераторы списков позволяют создавать и быстро заполнять списки.

Синтаксическая конструкция генератора списка предполагает наличие итерируемого объекта или итератора, на базе которого будет создаваться новый список, а также выражение, которое будет что-то делать с извлеченными из последовательности элементами перед тем как добавить их в формируемый список. 

>>> a = [1, 2, 3]
>>> b = [i+10 for i in a]
>>> a
[1, 2, 3]
>>> b
[11, 12, 13]

В примере выше генератором списка является выражение [i+10 for i in a]. Здесь a — итерируемый объект. В данном случае это другой список. Из него извлекается каждый элемент в цикле for. Перед for описывается действие, которое выполняется над элементом перед его добавлением в новый список.

Обратите внимание, что генератор создает новый список, а не изменяет существующий. Если надо изменить текущую переменную, ей надо присвоить новое значение:

>>> a = [1, 2, 3]
>>> a = [i+10 for i in a]
>>> a
[11, 12, 13]

Генераторы списков относятся к разряду «синтаксического сахара» языка программирования Python. Другими словами, без них можно обойтись:

>>> for index, value in enumerate(a):
...     a[index] = value + 10
... 
>>> a
[11, 12, 13]

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

>>> ls0 = [1,2,3]
>>> ls1 = ls0
>>> ls1.append(4)
>>> ls0
[1, 2, 3, 4]
>>> ls1 = [i+1 for i in ls1]
>>> ls1
[2, 3, 4, 5]
>>> ls0
[1, 2, 3, 4]

Здесь мы предполагаем, что изменение списка через одну переменную, будут видны через другую. Однако если изменить список генератором, то переменные будут указывать на разные списки.

Перебираемым в цикле for объектом может быть быть не только список. В примере ниже в список помещаются строки файла.

>>> lines = [line.strip() for line in open('text.txt')]
>>> lines
['one', 'two', 'three']

В генератор списка можно добавить условие:

>>> from random import randint
>>> nums = [randint(10, 20) for i in range(10)]
>>> nums
[18, 17, 11, 11, 15, 18, 11, 20, 10, 19]
>>> nums = [i for i in nums if i%2 == 0]
>>> nums
[18, 18, 20, 10]

Генераторы списков могут содержать вложенные циклы:

>>> a = "12"
>>> b = "3"
>>> c = "456"
>>> comb = [i+j+k for i in a for j in b for k in c]
>>> comb
['134', '135', '136', '234', '235', '236']

Генераторы словарей и множеств

Если в выражении генератора списка заменить квадратные скобки на фигурные, то можно получить не список, а словарь:

>>> a = {i:i**2 for i in range(11,15)}
>>> a
{11: 121, 12: 144, 13: 169, 14: 196}

При этом синтаксис выражения до for должен быть соответствующий словарю, то есть включать ключ и через двоеточие значение. Если этого нет, будет сгенерировано множество:

>>> a = {i for i in range(11,15)}
>>> a
set([11, 12, 13, 14])
>>> b = {1, 2, 3}
>>> b
set([1, 2, 3])

Генераторы

Выражения, создающие объекты-генераторы, похожи на выражения, генерирующие списки, словари и множества за одним исключением. Чтобы создать генераторный объект, надо использовать круглые скобки:

>>> a = (i for i in range(2, 8))
>>> a
<generator object <genexpr> at 0x7efc88787910>
>>> for i in a:
...     print(i)
... 
2
3
4
5
6
7

Второй раз перебрать генератор в цикле for не получится, так как объект-генератор уже сгенерировал все значения по заложенной в него «формуле». Поэтому генераторы обычно используются, когда надо единожды пройтись по итерируемому объекту.

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

Выражение, создающее генератор, это сокращенная запись следующего:

>>> def func(start, finish):
...     while start < finish:
...             yield start * 0.33
...             start += 1
... 
>>> a = func(1, 4)
>>> a
<generator object func at 0x7efc88787a50>
>>> for i in a:
...     print(i)
... 
0.33
0.66
0.99

Функция, содержащая yield, возвращает объект-генератор, а не выполняет свой код сразу. Тело функции исполняется при каждом вызове метода __next__(). В цикле for это делается автоматически. При этом функция сохраняет значения переменных от предыдущего вызова.

Если нет необходимости использовать функцию многократно, проще использовать выражение:

>>> b = (i*0.33 for i in range(1,4)) 
>>> b
<generator object <genexpr> at 0x7efc88787960>
>>> for i in b:
...     print(i)
... 
0.33
0.66
0.99

Глава 3. Генераторы

Добавлено 4 апреля 2020 в 01:43

Сохранить или поделиться

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

Ричард Фейнман

Содержание главы

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

Ричард Фейнман

Погружение

В каждом языке программирования есть одна такая особенность, сложно устроенная, но специально упрощённая штука. Если вы раньше писали на другом языке, можете и не обратить на это внимания, поскольку ваш старый язык не так сильно упрощал эту штуку (потому что он был занят тем, что сильно упрощал что-то другое). В этой главе вы изучите генераторы списков, словарей и множеств – три взаимосвязанные концепции, сконцентрированные вокруг одной очень мощной технологии. Но сначала я хочу немного отклониться от нашего повествования, чтобы рассказать вам о двух модулях, которые помогут вам передвигаться по вашей локальной файловой системе.

3.2 Работа с файлами и каталогами

Python 3 поставляется с модулем os, что означает «операционная система». Модуль os содержит множество функций для получения информации о локальных каталогах, файлах, процессах и переменных окружения (а в некоторых случаях, и для манипулирования ими). Python предлагает очень хороший унифицированный программный интерфейс для всех поддерживаемых операционных систем, так что ваши программы можно запускать на любом компьютере с минимальным количеством платформо-зависимого кода.

3.2.1 Текущий рабочий каталог

Всегда есть текущий рабочий каталог.

Когда ваше знакомство с Python только начинается, вы много времени проводите в интерактивной оболочке Python. На протяжении всей этой книги вы будете видеть примеры, выглядящие следующим образом:

  1. Импортирование какого-либо модуля из папки примеров
  2. Вызов функции из этого модуля
  3. Объяснение результата

Если вы ничего не знаете о текущем рабочем каталоге, то, возможно, шаг 1 окажется неудачным и будет порождено исключение типа ImportError. Почему? Потому что Python будет искать указанный модуль в пути поиска оператора import, но не найдёт его, потому что каталог examples не содержится в путях поиска. Чтобы исправить это, вы можете сделать одно из двух:

  1. либо добавить папку examples в путь поиска оператора import;
  2. либо сделать текущим рабочим каталогом папку examples.

Текущий рабочий каталог является неявным параметром, который Python постоянно хранит в памяти. Текущий рабочий каталог есть всегда, когда вы работаете в интерактивной оболочке Python, запускаете свой сценарий из командной строки или CGI-сценарий где-то на веб-сервере.

Модуль os содержит две функции для работы с текущим рабочим каталогом.

>>> import os                                            ①
>>> print(os.getcwd())                                   ②
C:\Python31
>>> os.chdir('/Users/pilgrim/diveintopython3/examples')  ③
>>> print(os.getcwd())                                   ④
C:\Users\pilgrim\diveintopython3\examples
  1. Строка 1. Модуль os поставляется вместе с Python; вы можете импортировать его когда угодно и где угодно.
  2. Строка 2. Используйте функцию os.getcwd() для получения значения текущего рабочего каталога. Когда вы работаете в графической оболочке Python, текущим рабочим каталогом является каталог из которого она была запущена. В Windows это зависит от того, куда вы установили Python; каталог по умолчанию c:\Python31. Если оболочка Python запущена из командной строки, текущим рабочим каталогом считается тот, в котором вы находились, когда запускали её.
  3. Строка 4. Используйте функцию os.chdir() чтобы сменить текущий рабочий каталог.
  4. Строка 5. Когда я вызывал функцию os.chdir(), я использовал путь в стиле Linux (косая черта, нет буквы диска), даже если на самом деле работал в Windows. Это одно из тех мест, где Python пытается стирать различия между операционными системами.

3.2.2 Работа с именами файлов и каталогов

Раз зашла речь о каталогах, я хочу обратить ваше внимание на модуль os.path. Он содержит функции для работы с именами файлов и каталогов.

>>> import os
>>> print(os.path. join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py'))              ①
/Users/pilgrim/diveintopython3/examples/humansize.py
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py'))               ②
/Users/pilgrim/diveintopython3/examples\humansize.py
>>> print(os.path.expanduser('~'))                                                               ③
c:\Users\pilgrim
>>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py'))  ④
c:\Users\pilgrim\diveintopython3\examples\humansize.py
  1. Строка 2. Функция os.path.join() составляет путь из одного или нескольких частичных путей. В данном случае она просто соединяет строки.
  2. Строка 4. Это уже менее тривиальный случай. Функция join добавит дополнительную косую черту (slash) к имени папки перед тем как дописать имя файла. В данном случае Python добавляет обратную косую черту (backslash) вместо обыкновенной, потому что я запустил этот пример в Windows. Если вы введёте данную команду в Linux или Mac OS X, вы увидите простую косую черту. Python может обратиться к файлу независимо от того, какой разделитель используется в пути к файлу.
  3. Строка 6. Функция os.path.expanduser() раскрывает путь, в котором используется символ ~ для обозначения домашнего каталога текущего пользователя. Функция работает на любой платформе, где у пользователя есть домашний каталог, включая Linux, Mac OS X и Windows. Функция возвращает путь без косой черты в конце, но для функции os.path.join() это не имеет значения.
  4. Строка 8. Комбинируя эти две функции, вы можете легко строить пути для папок и файлов в домашнем каталоге пользователя. Функция os.path.join() принимает любое количество аргументов. Я получил огромное удовольствие, когда обнаружил это, так как в других языках при разработке инструментальных средств мне приходилось постоянно писать глупую маленькую функцию addSlashIfNecessary(). В языке программирования Python умные люди уже позаботились об этом.

Модуль os.path также содержит функции для разбиения файловых путей, имён папок и файлов на их составные части.

>>> pathname = '/Users/pilgrim/diveintopython3/examples/humansize.py'
>>> os.path.split(pathname)                                        ①
('/Users/pilgrim/diveintopython3/examples', 'humansize.py')
>>> (dirname, filename) = os.path.split(pathname)                  ②
>>> dirname                                                        ③
'/Users/pilgrim/diveintopython3/examples'
>>> filename                                                       ④
'humansize.py'
>>> (shortname, extension) = os.path.splitext(filename)            ⑤
>>> shortname
'humansize'
>>> extension
'.py'
  1. Строка 2. Функция split дробит полный путь и возвращает кортеж, содержащий отдельно путь до каталога и имя файла.
  2. Строка 4. Помните, я рассказывал про то, как присваивать несколько значений за раз и как вернуть одновременно несколько значений из функции? Функция os.path.split() действует именно так. Можно присвоить возвращаемое из функции split значение кортежу из двух переменных. Каждая из переменных примет значение соответствующего элемента результирующего кортежа.
  3. Строка 5. Первая переменная – dirname – получит значение первого элемента кортежа, возвращаемого функцией os.path.split(), а именно путь до каталога.
  4. Строка 7. Вторая переменная – filename – примет значение второго элемента кортежа, возвращаемого функцией os.path.split(), а именно имя файла.
  5. Строка 9. Модуль os.path также содержит функцию os. path.splitext(), которая дробит имя файла и возвращает кортеж, содержащий отдельно имя и отдельно расширение файла. Можно использовать ту же технику, что и ранее для присваивания каждого из интересующих значений отдельным переменным.

3.2.3 Получение содержимого каталога

Модуль glob понимает символы-джокеры, использующиеся в командных оболочках.

Модуль glob – это ещё один инструмент из стандартной библиотеки Python. Это простой способ программно получить содержимое папки, а также он умеет использовать символы-джокеры, с которыми вы наверняка знакомы, если работали в командной строке.

>>> os.chdir('/Users/pilgrim/diveintopython3/')
>>> import glob
>>> glob.glob('examples/*.xml')                  ①
['examples\\feed-broken.xml',
 'examples\\feed-ns0.xml',
 'examples\\feed.xml']
>>> os.chdir('examples/')                        ②
>>> glob.glob('*test*.py')                       ③
['alphameticstest.py',
 'pluraltest1.py',
 'pluraltest2.py',
 'pluraltest3.py',
 'pluraltest4.py',
 'pluraltest5.py',
 'pluraltest6.py',
 'romantest1.py',
 'romantest10.py',
 'romantest2.py',
 'romantest3.py',
 'romantest4.py',
 'romantest5.py',
 'romantest6.py',
 'romantest7.py',
 'romantest8.py',
 'romantest9.py']
  1. Строка 3. Модуль glob принимает шаблон, содержащий символы-джокеры, и возвращает пути всех файлов и каталогов, соответствующих ему. В этом примере шаблон содержит путь к каталогу и «*.xml«, которому будут соответствовать все xml-файлы в каталоге examples.
  2. Строка 7. Теперь сделаем текущим рабочим каталог examples. Функция os.chdir() может принимать и относительные пути.
  3. Строка 8. Вы можете использовать несколько символов-джокеров в своём шаблоне. Этот пример находит все файлы в текущем рабочем каталоге, заканчивающиеся на .py и содержащие слово test где-нибудь в имени файла.

3.2.4 Получение сведений о файле

Любая современная операционная система хранит сведения о каждом файле (метаданные): дата создания, дата последней модификации, размер файла и т.д. Python предоставляет единый программный интерфейс для доступа к этим метаданным. Вам не надо открывать файл; всё, что требуется – имя файла.

>>> import os
>>> print(os.getcwd())                 ①
c:\Users\pilgrim\diveintopython3\examples
>>> metadata = os.stat('feed.xml')     ②
>>> metadata.st_mtime                  ③
1247520344.9537716
>>> import time                        ④
>>> time.localtime(metadata.st_mtime)  ⑤
time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17,
  tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
  1. Строка 2. Текущий рабочий каталог – папка с примерами.
  2. Строка 4.feed.xml – файл в папке с примерами. Вызов функции os.stat() возвращает объект, содержащий различные метаданные о файле.
  3. Строка 5.st_mtime – время изменения файла, но записано оно в ужасно неудобном формате. (Фактически это количество секунд, прошедших с начала «эры UNIX», начавшейся в первую секунду 1 января 1970 года. Серьёзно.)
  4. Строка 7. Модуль time является частью стандартной библиотеки Python. Он содержит функции для преобразований между различными форматами представления времени и часовыми поясами, для преобразования их в строки (str) и др.
  5. Строка 8. Функция time.localtime() преобразует время из формата «секунды с начала эры» (поле st_mtime, возвращённое функцией os. stat()) в более удобную структуру, содержащую год, месяц, день, час, минуту, секунду и т. д. Этот файл в последний раз изменялся 13 июля 2009 года, примерно в 17 часов, 25 минут.
# continued from the previous example
>>> metadata.st_size                              ①
3070
>>> import humansize
>>> humansize.approximate_size(metadata.st_size)  ②
'3.0 KiB'
  1. Строка 2. Функция os.stat() также возвращает размер файла в свойстве st_size. Размер файла feed.xml – 3070 байт.
  2. Строка 5. Вы можете передать свойство st_size в функцию approximate_size().

3.2.5 Получение абсолютных путей

В предыдущем разделе функция glob.glob() возвращала список относительных путей. В первом примере пути имели вид ‘examples\feed.xml‘, а во втором относительные пути были даже короче, например, ‘romantest1.py‘. Пока вы остаётесь в текущем рабочем каталоге, по этим относительным путям можно будет открывать файлы или получать их метаданные. Но если вы захотите получить абсолютный путь – то есть тот, который включает все имена каталогов до корневого или до буквы диска, вам понадобится функция os.path.realpath().

>>> import os
>>> print(os.getcwd())
c:\Users\pilgrim\diveintopython3\examples
>>> print(os.path.realpath('feed.xml'))
c:\Users\pilgrim\diveintopython3\examples\feed.xml

3.3 Генераторы списков

В генераторах списков можно использовать любые выражения Python.

С помощью генераторов списков можно легко отобразить один список в другой, применив некоторую функцию к каждому элементу.

>>> a_list = [1, 9, 8, 4]
>>> [elem * 2 for elem in a_list]           ①
[2, 18, 16, 8]
>>> a_list                                  ②
[1, 9, 8, 4]
>>> a_list = [elem * 2 for elem in a_list]  ③
>>> a_list
[2, 18, 16, 8]
  1. Строка 2. Чтобы понять, что здесь происходит, прочитайте генератор справа налево. a_list – отображаемый список. Python последовательно перебирает элементы списка a_list, временно присваивая значение каждого элемента переменной elem. Затем применяет функцию elem * 2 и добавляет результат в возвращаемый список.
  2. Строка 4. Генератор создаёт новый список, не изменяя исходный.
  3. Строка 6. Можно присвоить результат работы генератора списка отображаемой переменной. Python создаст новый список в памяти и, когда результат работы генератора будет получен, присвоит его исходной переменной.

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

>>> import os, glob
>>> glob.glob('*.xml')                                 ①
['feed-broken.xml', 'feed-ns0.xml', 'feed.xml']
>>> [os.path.realpath(f) for f in glob.glob('*.xml')]  ②
['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml',
 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml',
 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']
  1. Строка 2. Это выражение возвращает список всех .xml-файлов в текущем рабочем каталоге.
  2. Строка 4. Этот генератор принимает список всех .xml-файлов и преобразует его в список полных путей.

При генерировании списков можно также фильтровать элементы, чтобы отбросить некоторые значения.

>>> import os, glob
>>> [f for f in glob.glob('*.py') if os.stat(f).st_size > 6000]  ①
['pluraltest6.py',
 'romantest10.py',
 'romantest6.py',
 'romantest7.py',
 'romantest8.py',
 'romantest9.py']
  1. Строка 2. Чтобы профильтровать список, добавьте оператор if в конце генератора списка. Выражение, стоящее после оператора if, будет вычислено для каждого элемента списка. Если это выражение будет истинно, данный элемент будет обработан и включён в генерируемый список. В данной строке генерируется список всех .py-файлов в текущей директории, а оператор if фильтрует этот список, оставляя только файлы размером больше 6000 байт. Таких файлов только шесть, поэтому будет сгенерирован список из шести имён файлов.

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

>>> import os, glob
>>> [(os.stat(f).st_size, os.path.realpath(f)) for f in glob.glob('*.xml')]            ①
[(3074, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml'),
 (3386, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml'),
 (3070, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml')]
>>> import humansize
>>> [(humansize.approximate_size(os.stat(f).st_size), f) for f in glob.glob('*.xml')]  ②
[('3.0 KiB', 'feed-broken.xml'),
 ('3.3 KiB', 'feed-ns0.xml'),
 ('3.0 KiB', 'feed.xml')]
  1. Строка 2. Этот генератор ищет все .xml-файлы в текущем рабочем каталоге, получает размер каждого файла (вызывая функцию os.stat()), и создает кортеж из размера файла и абсолютного пути каждого файла (вызывая функцию os.path.realpath()).
  2. Строка 7. Этот генератор, основанный на предыдущем, вызывает функцию approximate_size(), передавая ей размер каждого .xml-файла.

3.4 Генераторы словарей

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

>>> import os, glob
>>> metadata = [(f, os.stat(f)) for f in glob.glob('*test*.py')]    ①
>>> metadata[0]                                                     ②
('alphameticstest.py', nt.stat_result(st_mode=33206, st_ino=0, st_dev=0,
 st_nlink=0, st_uid=0, st_gid=0, st_size=2509, st_atime=1247520344,
 st_mtime=1247520344, st_ctime=1247520344))
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*test*.py')}  ③
>>> type(metadata_dict)                                             ④
<class 'dict'>
>>> list(metadata_dict.keys())                                      ⑤
['romantest8.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest5.py',
 'pluraltest6.py', 'romantest7.py', 'romantest10.py', 'romantest4.py',
 'romantest9.py', 'pluraltest3.py', 'romantest1.py', 'romantest2.py',
 'romantest3.py', 'romantest5.py', 'romantest6.py', 'alphameticstest.py',
 'pluraltest4.py']
>>> metadata_dict['alphameticstest.py'].st_size                     ⑥
2509
  1. Строка 2. Это не генератор словаря, это генератор списка. Он находит все файлы с расширением .py, проверяет их имена, а затем создает кортеж из имени файла и метаданных файла (вызывая функцию os.stat()).
  2. Строка 3. Каждый элемент результирующего списка – кортеж.
  3. Строка 7. Это генератор словаря. Синтаксис подобен синтаксису генератора списка, но с двумя отличиями. Во-первых, он заключён в фигурные скобки, а не в квадратные. Во-вторых, вместо одного выражения для каждого элемента он содержит два, разделённые двоеточием. Выражение слева от двоеточия (в нашем примере f) является ключом словаря; выражение справа от двоеточия (в нашем примере os.stat(f)) – значением.
  4. Строка 8. Генератор словаря возвращает словарь.
  5. Строка 10. Ключи данного словаря – это просто имена файлов, полученные с помощью glob.glob('*test*.py').
  6. Строка 16. Значение, связанное с каждым ключом, получено с помощью функции os.stat(). Это означает, что в этом словаре мы можем по имени файла получить его метаданные. Один из элементов метаданных (st_size) – это размер файла. Размер файла alphameticstest.py – 2509 байт.

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

>>> import os, glob, humansize
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*')}                                  ①
>>> humansize_dict = {os.path.splitext(f)[0]:humansize.approximate_size(meta.st_size) \     
...                   for f, meta in metadata_dict.items() if meta.st_size > 6000}          ②
>>> list(humansize_dict.keys())                                                             ③
['romantest9', 'romantest8', 'romantest7', 'romantest6', 'romantest10', 'pluraltest6']
>>> humansize_dict['romantest9']                                                            ④
'6.5 KiB'
  1. Строка 2. В этом выражении берётся список файлов в текущей директории (glob.glob('*')), для каждого файла определяются его метаданные (os.stat(f)) и строится словарь, ключами которого выступают имена файлов, а значениями – метаданные каждого файла.
  2. Строка 4. Этот генератор строится на основе предыдущего. Отфильтровываются файлы меньше 6000 байт (if meta.st_size > 6000). Отобранные элементы используются для построения словаря, ключами которого являются имена файлов без расширения (os. path.splitext(f)[0]), а значениями – приблизительный размер каждого файла (humansize.approximate_size(meta.st_size)).
  3. Строка 5. Как вам уже известно из предыдущего примера, всего имеется шесть таких файлов, следовательно, в этом словаре шесть элементов.
  4. Строка 7. Значение для каждого ключа представляет из себя строку, полученную вызовом функции approximate_size().

3.4.1 Другие интересные штуки, которые можно делать с помощью генераторов словарей

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

>>> a_dict = {'a': 1, 'b': 2, 'c': 3}
>>> {value:key for key, value in a_dict.items()}
{1: 'a', 2: 'b', 3: 'c'}

Конечно же, это сработает, только если значения элементов словаря неизменяемы, как, например, строки или кортежи.

>>> a_dict = {'a': [1, 2, 3], 'b': 4, 'c': 5}
>>> {value:key for key, value in a_dict.items()}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
TypeError: unhashable type: 'list'

3.5 Генераторы множеств

Нельзя оставить за бортом и множества, они тоже могут создаваться с помощью генераторов. Единственное отличие – вместо пар ключ:значение, они строятся на основе одних значений.

>>> a_set = set(range(10))
>>> a_set
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> {x ** 2 for x in a_set}           ①
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}
>>> {x for x in a_set if x % 2 == 0}  ②
{0, 8, 2, 4, 6}
>>> {2**x for x in range(10)}         ③
{32, 1, 2, 4, 8, 64, 128, 256, 16, 512}
  1. Строка 4. В качестве входных данных генераторы множеств могут получать другие множества. Этот генератор рассчитывает квадраты множества чисел в диапазоне от 0 до 9.
  2. Строка 6. Подобно генераторам списков и словарей, генераторы множеств могут содержать условие if для проверки каждого элемента перед включением его в результирующее множество.
  3. Строка 8. На вход генераторы множеств могут принимать не только множества, но и любые другие последовательности.

3.6 Материалы для дальнейшего чтения

Источник:

  • Mark Pilgrim. Dive Into Python 3

Теги

PythonОбучениеПрограммированиеЯзыки программирования

Сохранить или поделиться

Этот ИИ составляет слова, которых вы даже не найдете в словаре

Меня ничто не привлекает Doodlewinkled как AI. Ой, что, вы никогда раньше не слышали об этом слове? Я не виноват, что вы черпаете свой словарный запас из традиционных словарей, составленных хунань. Пополняю свой словарный запас этим словом doesnotexist.com.

Да, верно: это словарный инструмент на базе искусственного интеллекта, который составляет слова и определения, которые вы не найдете ни в одном обычном словаре, таком как Merriam-Webster или Oxford English Dictionary, — и это просто замечательно.

Моим фаворитом на данный момент должен быть Helicopterygium , или, как это определяет это слово doesnotexist.com:

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

Пример: «Вертоптеригиумы варьируются от крошечных куколок до полных двустворчатых моллюсков.”

Необычный словарь следует давней традиции сгенерированных искусственным интеллектом несуществующих вещей, таких как ноги, мемы, еда, кошки, объявления Airbnb, пушистые портреты и даже люди.

Словарь основан на алгоритме обработки естественного языка Transformers и печально известном текстовом генераторе GPT-2 на базе искусственного интеллекта OpenAI, который, по словам компании, может быть слишком опасным для публикации в открытом доступе (прежде чем он в конечном итоге передумает).

Каким бы забавным это ни было, thisworddoesnotexist.com не идеален.

Время от времени вы можете наткнуться на уже существующее слово. Это потому, что сложно составить исчерпывающий список всех слов английского языка. К счастью, разработчик Томас Димсон создал функцию черного списка, которая исключает слова, которые уже присутствуют в других словарях, и периодически обновляется. Так что шансов, что все эти скучные уже существующие слова скоро уйдут.

А пока пополните свой словарный запас некоторыми классными словами, которые никто никогда не слышал, нажав здесь.Это поднимет твои навыки говорения на совершенно новом уровне, ты, абсолютный карпот .

Опубликовано 14 мая 2020 г., 12:12 UTC

Микс

14 мая 2020 г. — 12:12 UTC

GitHub — CleverPumpkin / Proguard-Словари-Генератор

Gradle Plugin, который генерирует рандомизированные словари для proguard

Протестировано на подключаемом модуле Android Gradle версии 3.5.0 и 3.6.0-alpha10

Как добавить

Добавьте в корневой проект файл build.gradle :

для Groovy

 buildscript {
    репозитории {
        maven {
            url "https://plugins. gradle.org/m2/"
        }
    }

    dependencies {
        путь к классам "gradle.plugin.ru.cleverpumpkin.proguard-dictionaries-generator: plugin: 1.0.8"
    }
} 

для Kotlin DSL

 buildscript {
    репозитории {
        maven {
            url = uri ("https: // plugins.gradle.org/m2/ ")
        }
    }
    dependencies {
        classpath ("gradle.plugin.ru.cleverpumpkin.proguard-dictionaries-generator: plugin: 1.0.8")
    }
} 

Простая конфигурация

Добавьте в свой модуль приложения файл build.gradle :

для Groovy

 применить плагин: "ru.cleverpumpkin.proguard-dictionaries-generator"

proguardDictionaries {
    DictionaryNames = [
        "сборка / класс-словарь",
        "сборка / пакет-словарь",
        "сборка / обфускация-словарь"
    ]
} 

для Kotlin DSL

 плагинов {
    id ("ru.cleverpumpkin.proguard-словарей-генератор ")
}

proguardDictionaries {
    DictionaryNames = listOf (
         "сборка / класс-словарь",
         "сборка / пакет-словарь",
         "сборка / обфускация-словарь"
      )
} 

Эта установка создаст файлы class-dictionary.txt , package-dictionary.txt
и obfuscation-dictionary.txt в подкаталоге build модуля, к которому подключаемый модуль
применяется. Вы можете указать любой каталог относительно корня вашего модуля .

Эти файлы будут создаваться с нуля при каждой сборке, поэтому у вас будут разные словари.
для каждой сборки.

Расширенная конфигурация

для Groovy

 proguardDictionaries {
    DictionaryNames = ["любой", "файлы," вы "," хотите "]
    minLineLength 20 // Значение по умолчанию: 10
    maxLineLength 50 // Значение по умолчанию: 30
    linesCountInDictionary 50000 // Значение по умолчанию: 30000
} 

для Kotlin DSL

 proguardDictionaries {
    DictionaryNames = listOf («любой», «файлы», «ты», «хочу»)
    minLineLength = 20 // Значение по умолчанию: 10
    maxLineLength = 50 // Значение по умолчанию: 30
    linesCountInDictionary = 50000 // Значение по умолчанию: 30000
} 

Как использовать результат вывода плагина

Добавьте к своим proguard-rules. pro файл:

  -obfuscationdictionary build / obfuscation-dictionary.txt
-classobfuscationdictionary build / class-dictionary.txt
-packageobfuscationdictionary build / package-dictionary.txt
  

Номер класса программы, выпуск

Важно: , если окончательное количество классов в * .apk после сжатия кода превышает
linesCountInDictionary значение
, тогда все остальные классы будут названы по умолчанию
начиная с первых букв алфавита.

Таким образом, значение linesCountInDictionary должно быть больше, чем количество программных классов после
сжатие кода.

Размер словаря по умолчанию составляет 30000 строк.

Вы можете узнать количество классов в вашем * .apk , выполнив следующие шаги:

  1. При выполнении сборки в Android Studio найдите последний шаг Optimizing в консоли,
    е. грамм. «Оптимизация (Шаг 5/5)».
  2. Проверьте значение в строке «Конечное количество классов программы».

или:

  1. Перетащите файл * .apk в Android Studio (или откройте его через меню Build ->
    Проанализировать APK … ).
  2. В окне APK Analyzer выберите файл classes.dex . Проверить значение в строке
    «Этот файл dex определяет классы X …»
  3. Если в APK присутствует больше файлов * .dex , выберите каждый из них и просуммируйте номера всех классов.

Наконец, установите значение linesCountInDictionary немного больше, чем полученное число
программных классов, чтобы сохранить дополнительное пространство для роста приложения (например,грамм. если программа содержит
9802 классов, можно установить значение 12000).

Proguard и R8 поддерживают

Плагин

запускается автоматически, когда плагин Android выполняет задачу Proguard или R8:

  • transformClassesAndResourcesWithR8For {BuiltFlavor} {BuiltType}
  • transformClassesAndResourcesWithProguardFor {BuiltFlavor} {BuiltType}

Вам не нужно указывать ничего особенного, чтобы это работало.

Словарь алфавита

Пробелы, знаки препинания, повторяющиеся слова и комментарии после знака # игнорируются.
в словарях Proguard.Таким образом, сгенерированный файл может содержать любые символы, кроме перечисленных выше.

https://www.guardsquare.com/en/products/proguard/manual/usage#obfuscationoptions

Вклад

Вам нужно протестировать плагин локально, верно?
Для локальной сборки плагина uploadArchives используется задача gradle, которая помещает плагин в
Папка PROJECT_DIR / plugin / pluginRepo .
Плагин нужно добавить в buildscript проекта.
В первый раз после клонирования проекта локально созданный плагин отсутствует, поэтому плагин должен быть загружен с портала плагинов Gradle, поэтому artifactId равен
град.plugin.ru.cleverpumpkin.proguard-dictionaries-generator: плагин
После синхронизации проекта загрузите плагин локально и установите artifactId равным ru.cleverpumpkin.proguard-dictionaries-generator: plugin .
Вы можете найти artifactId здесь

Авторы

Разработал Сергей Чуприн ([email protected])
Поддержал Руслан Арсланов ([email protected])

Определение генератора от Merriam-Webster

ген · эр · а · тор

| \ ˈJe-nə-rā-tər

\

а

: Аппарат, в котором образуется пар или газ

б

: Машина, с помощью которой механическая энергия преобразуется в электрическую.

2

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

конкретно

: образующая

генератор словаря паролей

Мне нужно было создать словарь паролей, который охватил бы все возможные комбинации для определенного набора символов.Я сначала научился программировать на Python, поэтому сначала собирался начать с него. Перед написанием программы я решил поискать в Google и посмотреть, решал ли кто-нибудь еще эту проблему через Python, как оказалось. Siph0n разместил свой код Python для создания словаря паролей на форумах BackTrack. Я хотел разместить его здесь как зеркало и обсудить последствия создания словаря паролей со всеми возможными комбинациями. Ниже представлен код Python.

f = open (‘wordlist’, ‘w’)

def xselections (items, n):
if n == 0: yield []
else:
для i в xrange (len (items)):
для ss в xselections (items, n-1):
yield [items [i]] + ss

# Numbers = 48 — 57
# Capital = 65 — 90
# Lower = 97 — 122
numb = range (48,58)
cap = range (65,91)
low = range (97,123)
choice = 0
while int (choice) not in range (1,8):
choice = raw_input (» ‘
1) Числа
2) Заглавные буквы
3) Строчные буквы
4) Цифры + заглавные буквы
5) Цифры + прописные буквы
6) Цифры + заглавные буквы + строчные буквы
7) Заглавные буквы + строчные буквы
: » ‘)

choice = int (выбор)
Poss = []
if choice == 1:
Poss + = Numb
elif choice == 2:
Poss + = cap
elif choice == 3:
Poss + = Low
elif choice == 4 :
Poss + = Numb
Poss + = cap
Poss + = Cap
Poss + = Low
ELIF Выбор == 6:
Poss + = Numb
Pos + = Cap
Pos + = Low
ELIF choice == 7:
Pos + = cap
Poss + = Low

bigList = []
для i в Pos:
bigList.append (str (chr (i)))

MIN = raw_input («Каков минимальный размер слова?»)
MIN = int (MIN)
MAX = raw_input («Каков максимальный размер слова? «)
MAX = int (MAX)
для i в диапазоне (MIN, MAX + 1):
для s в xselections (bigList, i): f.write (». Join (s) + ‘\ n’)

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

Как установить и использовать программу

  1. У вас должен быть установлен Python. Если вы используете Linux (а должно быть), то он, вероятно, уже установлен. Если вы используете Windows, вам необходимо загрузить Python.
  2. Теперь, когда у вас установлен Python, просто скопируйте и вставьте приведенный выше код в текстовый файл и назовите его passwordDictionaryGenerator.py. Расширение .py необходимо, потому что именно так Python распознает код, который предполагается выполнить.
  3. Измените соответствующие переменные в программе.Единственные переменные, которые вы можете захотеть изменить, это numb, cap и low. Эти переменные содержат диапазоны эквивалентов ASCII для букв и цифр, которые вы будете использовать для создания словаря. Вы можете изменить эти переменные так, чтобы ваш словарь содержал не a-z, а только a-k, я оставлю это на ваше усмотрение.
  4. Теперь, чтобы запустить программу, просто введите

    пароль PythonDictionaryGenerator.py

    Вам нужно будет ответить на вопросы о том, какой набор символов вы хотите использовать и насколько длинным / коротким будет ваш словарь паролей.После того как вы ответите на вопросы, может показаться, что программа ничего не делает, но это так, она вернет вас обратно в командную строку после завершения программы. Результатом будет файл с именем wordlist.

Итак, теперь у вас есть эта классная программа, которая может сгенерировать для вас словарь паролей. Насколько большим (размер МБ, ГБ, ТБ и т. Д.) Будет этот словарь? Сколько времени потребуется на создание этого словаря? Давайте сначала рассмотрим вопрос о размере, так как он также поможет нам рассчитать время.Ключом к вычислению размера является математический термин, называемый перестановками. Перестановки — это простое уравнение для определения количества слов для этого конкретного набора символов и длины слова. Основное уравнение приведено ниже.

n r

n = общий набор символов (например, a-z + A-Z + 0-9 = 62)

r = длина слова

Теперь вам нужно вычислить n r для каждой длины, чтобы получить все возможные комбинации. Итак, для пароля из 6 цифр ваше уравнение будет выглядеть следующим образом.

n 6 + n 5 + n 4 + n 3 + n 2 + n 1 = все возможные комбинации

Давайте попробуем пример, где наш набор символов — это a-z (n = 26), а наш пароль не длиннее 6 (r = 1-6) цифр. Сколько слов будет в нашем словаре?

26 6 + 26 5 + 26 4 + 26 3 + 26 2 + 26 1 = 321 272 406 = общее количество слов

Итак, теперь мы понимаем, как рассчитать общее количество слов в нашем словаре.Как это связано с размером? По большей части, если длина пароля равна x, тогда размер в байтах будет x + 1 для этой конкретной строки. Затем все, что нам нужно сделать, это умножить каждое n r на размер этой конкретной строки, чтобы получить размер для этой конкретной длины. Это могло показаться действительно запутанным, поэтому, надеюсь, следующий график немного это проясняет.

Я создал этот словарь, это заняло около 30 минут. Оказывается, размер совпал с моими расчетами.

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

  • набор символов = a – z, A – Z и 0–9
  • длина пароля = 1-8
  • п = 62
  • г = 1-8

С этими параметрами размер нашего словаря увеличивается до 1800 терабайт или 1,8 петабайта. Взгляните на таблицу ниже.

Вы видите, как быстро размер увеличивается. Не знаю, как вы, но у меня нет двухпетабайтного диска. Создать этот словарь просто невозможно. Я подсчитал время, которое, вероятно, потребуется для создания этого словаря, оно составило около 11 дней. Таким образом, время на создание такого словаря — ничто по сравнению с объемом памяти, необходимым для его размещения. Мало того, что я не знаю, что многие приложения могут обрабатывать большой словарь в качестве входных данных, так что это еще один фактор, который вы должны учитывать при создании словаря.

Я оставлю на ваше усмотрение расчет времени, необходимого для создания этих словарей. Основная идея состоит в том, что вы можете запустить программу python для пароля определенной длины в течение установленного времени, а затем экстраполировать форму туда. По большей части, время не имеет значения, важна память. В концепциях, о которых я здесь говорил, нет ничего нового. Идея создания пароля пришла ко мне и моим коллегам, когда мы думали о способах тестирования беспроводной инфраструктуры WPA. Атаковать WPA можно в автономном режиме, поэтому мы подумали о создании словаря для этого.Через несколько часов мы вскоре осознали сложность создания такого большого словаря. На самом деле это была хорошая новость, потому что это означало, что злоумышленнику будет чрезвычайно сложно атаковать точку доступа WPA со сложным паролем. Renderman и Церковь Wi-Fi думали об этой проблеме задолго до меня и придумали несколько радужных таблиц, которые помогут проверить надежность вашей точки доступа WPA. Вы не можете создать словарь с каждой комбинацией длинных паролей, лучше всего создать словарь с наиболее «общими» паролями, что тоже непростая задача.

Мораль этой истории — использовать длинные сложные пароли с высоким набором символов, но вы это уже знали. Поэтому я просто предположил, что эта программа несколько бесполезна, ну, это так, но это не так. Вы можете использовать эту программу для создания небольшого словаря, но о большом словаре (больше пары терабайт), вероятно, не может быть и речи. Так что используйте эту программу и дайте мне знать, каковы ваши результаты, мне всегда интересно ваше мнение. Счастливого взлома.

Основы Python: понимание списков, понимание словаря и выражения генератора

Одним из основных преимуществ Python перед другими языками программирования является его краткий, читаемый код.Однако даже внутри самого языка Python есть способы писать более элегантный код, который позволяет добиться того же конечного результата более эффективно. Составление списков, понимание словаря и выражения генератора — три убедительных примера таких элегантных выражений. В этом посте мы рассмотрим циклы for, понимание списков, понимание словаря и выражения генератора, чтобы продемонстрировать, как каждое из них может сэкономить ваше время и упростить разработку Python.

Составление списков и словарей в Python

Чтобы понять основы работы со списками и словарями, давайте сначала перейдем к для -петель.

для — петли

В Python цикл for- идеально подходит для обработки повторяющихся задач программирования, поскольку его можно использовать для итерации по последовательности, такой как список, словарь или строка.

Давайте рассмотрим простой пример использования списка:

  слов = ['кот', 'окно', 'мяч']  

для x прописью:

print (x)

Результат — каждый элемент выводится один за другим в отдельной строке:

  кот  

окно

мяч

По мере того, как вы разбираетесь с более сложным для -циклов, а затем составляете список понятий и словаря, полезно понимать логику, стоящую за ними.

Цикл для работает, беря первый элемент итерации (в приведенном выше случае — список) и проверяя, существует ли он. Если это так, требуемое действие выполняется (в приведенном выше случае печатает ). Затем цикл начинается снова и ищет следующий элемент. Если этот элемент существует, требуемое действие выполняется снова. Это поведение повторяется до тех пор, пока не перестанут быть найдены элементы и цикл не завершится.

Использование функции range ()

Очень полезная функция range () является встроенной функцией Python и используется почти исключительно с для -циклов.По сути, его цель — создать последовательность чисел.

Давайте посмотрим на примере, чтобы увидеть, как это работает:

  для i в диапазоне (5):  

print (i)

Результат:

  0  

1

2

3

4

Имейте в виду, что функция range () начинается с 0, поэтому range (5) вернет числа от 0 до 4, а не от 1 до 5.

По умолчанию последовательность начинается с 0, увеличивается с шагом 1 и заканчивается указанным числом. Однако можно определить первый элемент, последний элемент и размер шага как диапазон (first, last, step_size).

Составление списков

Понимание списков

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

Компоненты списков состоят из скобок, содержащих выражение, за которым следует предложение for, то есть [выражение-элемент для элемента в итераторе] или [x для x в итераторе] , а затем могут следовать дальнейшие для или , если предложений: [элемент-выражение для элемента в итераторе, если условно] .

Давайте рассмотрим несколько примеров, чтобы увидеть, как они работают:

  x = [i для i в диапазоне (10)]  

Выдает результат:

  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  

И с использованием условного оператора:

  x = [i для i в диапазоне (10), если i> 5]  

Возвращает список:

  [6, 7, 8, 9]  

, поскольку выполняется условие i> 5 .

Помимо того, что списки более лаконичны и удобочитаемы, чем их эквиваленты for-loop, они также значительно быстрее.

Понятия вложенных списков

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

Для демонстрации рассмотрим следующий пример:

  [(x, y) для x в [1,2,3] для y в [3,1,4], если x! = Y]  

Результат:

  [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]  

Сложные выражения и вложенные функции

Вы также можете использовать функции и сложные выражения внутри списков.

Например:

  from math import pi # import pi number from math module  

[str (round (pi, i)) for i in range (1, 6)]

Возврат:

  [«3,1», «3,14», «3,142», «3,1416», «3,14159»]  

Понимание списков

идеально подходит для создания более компактных строк кода. Их также можно использовать для полной замены на -циклы, а также функций map (), filter (), и reduce () , которые часто используются вместе с лямбда-функциями.

Толкования словаря

В Python понимание словаря очень похоже на понимание списка — только для словарей. Они предоставляют элегантный метод создания словаря из итерируемого словаря или преобразования одного словаря в другой.

Синтаксис аналогичен синтаксису, используемому для понимания списка, а именно {ключ: выражение-элемент для элемента в итераторе}, , но обратите внимание на включение пары выражений (ключ: значение) .

Давайте рассмотрим простой пример создания словаря.Код можно записать как

  dict ([(i, i + 10) for i in range (4)])  

, что эквивалентно :

  {i: i + 10 для i в диапазоне (4)}  

В обоих случаях возвращаемое значение:

  {0: 10, 1: 11, 2: 12, 3: 13}  

За этим базовым синтаксисом также может следовать дополнительное для или , если предложения : {ключ: выражение-элемент для элемента в итераторе при условии} .

Использование оператора if позволяет отфильтровывать значения для создания нового словаря.

Например:

  {i: i + 10 для i в диапазоне (10), если i> 5}  

Возврат:

  {6: 16, 7: 17, 8: 18, 9: 19}  

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

Понимание вложенного словаря

В Python понимание словаря также может быть вложенным для создания одного понимания словаря внутри другого.

Например:

  {(k, v): k + v для k в диапазоне (2) для v в диапазоне (2)}  

Возврат:

  {(0, 0): 0, (0, 1): 1, (1, 0): 1, (1, 1): 2}  

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

Генератор выражений в Python

Выражения генератора

— еще один пример высокопроизводительного способа написания кода более эффективно, чем традиционные итераторы на основе классов. Чтобы лучше понять выражения генератора, давайте сначала посмотрим, что такое генераторы и как они работают.

Функции генератора

Итераторы на основе классов в Python часто многословны и требуют больших накладных расходов.С другой стороны, генераторы могут выполнять ту же функцию, автоматически сокращая накладные расходы.

Генераторы

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

Генераторы работают следующим образом:

  • Когда вызывается функция генератора, она не выполняется немедленно, а возвращает объект генератора.

  • Код не будет выполняться до тех пор, пока для объекта-генератора не будет вызвано next () .

  • После вызова yield управление временно передается обратно вызывающей стороне, и функция приостанавливается.

  • Локальные переменные и состояние их выполнения сохраняются между вызовами.

  • StopIteration вызывается автоматически по завершении функции.

Функции генератора можно записать как:

  def gen ():  

для x в диапазоне (10):

yield x ** 2

Генератор выражений

Выражения генератора

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

Тот же код, что и в приведенном выше примере, можно записать как:

  г = (x ** 2 для x в диапазоне (10))  

Еще одна ценная особенность генераторов — это их способность фильтровать элементы по условиям.

Давайте посмотрим на пример :

  gen_exp = (i ** 2 для i в диапазоне (10), если i> 5)  

для i в gen_exp:

print (i)

Возврат:

  36  

49

64

81

Выражения генератора против представлений списков

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

Например, выражение генератора можно записать как:

  (i ** 2 для i в диапазоне (10), если i> 5)  

Сравните это со списком, который записывается как:

  [i ** 2 для i в диапазоне (10), если i> 5]  

Однако они различаются типом возвращаемых данных. В то время как понимание списка вернет весь список, выражение генератора вернет объект генератора. Хотя значения такие же, как и в списке, доступ к ним осуществляется по одному с помощью функции next () .

С точки зрения скорости, составление списков обычно быстрее, чем выражения генератора, хотя и не в тех случаях, когда размер обрабатываемых данных превышает размер доступной памяти. Вдобавок к этому, поскольку выражения-генераторы производят значения только по запросу, в отличие от представлений списков, которые требуют памяти для создания всего списка, выражения-генераторы гораздо более эффективны с точки зрения памяти.

Выражения генератора

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

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

Завершение

Циклы for Python

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

Точно так же генераторы и выражения генератора предлагают высокопроизводительный и простой способ создания итераторов.Несмотря на то, что синтаксис подобен пониманию списков, выражения генератора возвращают значения только по запросу, в отличие от всего списка в первом случае. В результате они используют меньше памяти и, следовательно, более эффективны.

генератор уникальных имен — npm

Более 50 000 000 комбинаций имен из коробки

Что такое генератор уникальных имен?

Генератор уникальных имен — это древовидный пакет узлов для генерации случайных и уникальных имен.

Он поставляется со списком словарей «из коробки», но вы также можете предоставить свои собственные.

Документы

Эта документация предназначена для генератора уникальных имен v4.

Если вы используете версию библиотеки 3.x, см.
v3 Документы

Для версий 1 и 2, пожалуйста, обратитесь к
v2 Документы

Переход на v4

Если вы хотите перейти со старой версии библиотеки на v4, прочтите руководство по миграции

Содержание

Предпосылки

Для этого проекта требуется NodeJS (как минимум версия 6) и NPM.Node и NPM действительно просты в установке.
Чтобы убедиться, что они доступны на вашем компьютере,
попробуйте выполнить следующую команду.

 $ node --version
Версия 7.10.1

$ npm --version
4.2.0 

Установка

ПЕРЕД УСТАНОВКОЙ: , пожалуйста, ознакомьтесь с предварительными условиями

Установите пакет с помощью npm или Yarn

 $ npm генератор уникальных имен i -S 

Или используя пряжу

 $ пряжа добавить генератор уникальных имен 

Использование

 const {uniqueNamesGenerator, прилагательные, цвета, животные} = require ('генератор уникальных имен');

const randomName = uniqueNamesGenerator ({словари: [прилагательные, цвета, животные]}); // big_red_donkey

const shortName = uniqueNamesGenerator ({
  словари: [прилагательные, животные, цвета], // цвета можно не указывать, так как они не используются
  длина: 2
}); // большой осел 

Поддержка машинописного текста

Этот пакет экспортирует файл определения типа, чтобы вы могли использовать его прямо из коробки,
внутри вашего проекта Typescript.

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const customConfig: Config = {
  словари: [прилагательные, цвета],
  разделитель: '-',
  длина: 2,
};

const randomName: string = uniqueNamesGenerator ({
  словари: [прилагательные, цвета, животные]
}); // big_red_donkey

const shortName: строка = uniqueNamesGenerator (customConfig); // большой осел 

API

uniqueNamesGenerator (параметры)

Возвращает строку со случайным сгенерированным именем

варианты

Тип: Конфигурация

словари

Тип: массив

требуется: правда

Это массив словарей.Каждый словарь — это массив строк, содержащих слова, которые будут использоваться для создания строки.

Предоставленные словари могут быть импортированы из библиотеки как отдельные модули и предоставлены в желаемом порядке.

 импортировать {uniqueNamesGenerator, прилагательные, цвета, животные} из «генератора уникальных имен»;

const shortName: string = uniqueNamesGenerator ({
  словари: [цвета, прилагательные, животные]
}); // red_big_donkey 

Подробнее о словарях и о том, как ими пользоваться, читайте в разделе Словари.

сепаратор

Тип: строка

требуется: ложь

По умолчанию: _

Разделитель строк, используемый для разделения сгенерированных слов.
Разделитель по умолчанию — _ .

длина

Тип: номер

требуется: ложь

По умолчанию: 3

Значение по умолчанию — 3 , и будет возвращено имя, состоящее из 3 слов.Эти значения должны быть равны количеству определенных словарей (по умолчанию 3) или быть меньшими.
Установка длины на значение 4 вызовет ошибку, если предоставлено только 3 словаря.

стиль

Тип: нижний корпус | upperCase | капитал

требуется: ложь

По умолчанию: нижний регистр

Значение по умолчанию — lowerCase и будет возвращать имя в нижнем регистре.
Если установить значение upperCase , слова будут возвращаться со всеми буквами в верхнем регистре.Опция capital будет использовать каждое слово уникального имени с заглавной буквы

.

 импортировать {uniqueNamesGenerator, прилагательные, цвета, животные} из «генератора уникальных имен»;

const capitalizedName: string = uniqueNamesGenerator ({
  словари: [цвета, прилагательные, животные],
  стиль: 'капитал'
}); // Red_Big_Donkey

const upperCaseName: string = uniqueNamesGenerator ({
  словари: [цвета, прилагательные, животные],
  стиль: 'верхний регистр'
}); // RED_BIG_DONKEY

const lowerCaseName: string = uniqueNamesGenerator ({
  словари: [цвета, прилагательные, животные],
  стиль: 'нижний регистр'
}); // red_big_donkey 
семена

Тип: номер

требуется: ложь

Начальное число используется, когда требуется детерминированно сгенерировать имя.Пока предоставленное начальное число одинаково, сгенерированное имя также всегда будет таким же.

Словари в наличии

Числа

Это динамический словарь. Подробнее читайте в разделе «Словарь чисел»

.

Прилагательные

Список из более чем 1400 прилагательных, готовых для использования

 импортировать {uniqueNamesGenerator, Config, прилагательные} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные]
}

const characterName: string = uniqueNamesGenerator (config); // большой 
Животные

Список из более чем 350 животных, готовых к употреблению

 импортировать {uniqueNamesGenerator, Config, animals} из генератора уникальных имен;

const config: Config = {
  словари: [животные]
}

const characterName: string = uniqueNamesGenerator (config); // осел 
Цвета

Список из более чем 50 различных цветов

 импортировать {uniqueNamesGenerator, Config, colors} из генератора уникальных имен;

const config: Config = {
  словари: [цвета]
}

const characterName: string = uniqueNamesGenerator (config); // красный 
Страны

Список из более чем 250 разных стран

 импортировать {uniqueNamesGenerator, Config, countries} из генератора уникальных имен;

const config: Config = {
  словари: [страны]
}

const characterName: string = uniqueNamesGenerator (config); // Объединенные Арабские Эмираты 
Имена

Список из более чем 4900 уникальных имен

 импортировать {uniqueNamesGenerator, Config, names} из генератора уникальных имен;

const config: Config = {
  словари: [имена]
}

const characterName: string = uniqueNamesGenerator (config); // Вайнона 
Языки

Список языков

 импортировать {uniqueNamesGenerator, Config, languages} из генератора уникальных имен;

const config: Config = {
  словари: [языки]
}

const characterName: string = uniqueNamesGenerator (config); // полироль 
Звездные войны

Список из более чем 80 уникальных имен персонажей из «Звездных войн

».

 импортировать {uniqueNamesGenerator, Config, starWars} из генератора уникальных имен;

const config: Config = {
  словари: [звездные войны]
}

const characterName: string = uniqueNamesGenerator (config); // Хан Соло 

Словари по умолчанию

По умолчанию библиотека генератора уникальных имен поставляется с 3 словарями из коробки, так что вы можете их использовать.
сразу.Однако, начиная с версии 4 библиотеки, вы должны явно указать словари в
объект конфигурации.
Это сделано для уменьшения размера пакета и разрешения встряхивания дерева для удаления лишних словарей из пакета, когда
используя нестандартные.

Новый синтаксис для использования словарей по умолчанию следующий:

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные, цвета, животные]
}

const characterName: string = uniqueNamesGenerator (config); // red_big_donkey 

Пользовательские словари

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

Вы можете легко сделать это, используя опцию словарей.

 импортировать {uniqueNamesGenerator} из генератора уникальных имен;

const starWarsCharacters = [
  'Хан Соло',
  "Джабба Хатт",
  'R2-D2',
  "Люк Скайуокер",
  'Принцесса Лея Органа'
];
const colors = [
  "Зеленый", "красный", "желтый", "черный"
]

const characterName: string = uniqueNamesGenerator ({
  словари: [цвета, персонажи звездных войн],
  длина: 2,
  разделитель: ' '
}); // Зеленый Люк Скайуокер 

Словарь чисел

Вы можете легко генерировать случайные числа внутри своего уникального имени с помощью помощника словаря Numbers.

 импортировать {uniqueNamesGenerator, NumberDictionary} из генератора уникальных имен;

const numberDictionary = NumberDictionary.generate ({мин: 100, макс: 999});
const characterName: string = uniqueNamesGenerator ({
словари: [['Dangerous'], ['Snake'], numberDictionary],
  длина: 3,
  разделитель: '',
  стиль: 'капитал'
}); // Опасная змея123 

Словарь чисел API

генерировать (параметры)

Возвращает строку со случайным сгенерированным числом от 1 до 999

варианты

Тип: Конфигурация

мин

Тип: номер

требуется: ложь

по умолчанию: 1

Минимальное значение, которое должно быть возвращено как случайное число

макс

Тип: номер

требуется: ложь

по умолчанию: 999

Максимальное значение, которое должно быть возвращено как случайное число

длина

Тип: номер

требуется: ложь

Длина возвращаемого случайно сгенерированного числа.

Установка длины 3 всегда будет возвращать случайное число от 100 до 999 . Это то же самое, что и установка 100 и 999 как мин. и макс. вариант.

Примечание Если задано, игнорируются любые предоставленные опции мин. и макс. .

Объединение пользовательских и предоставленных словарей

Вы можете повторно использовать словари, предоставленные библиотекой.
Просто импортируйте те, которые вам нужны, и используйте их прямо в своем приложении.

 импортировать {uniqueNamesGenerator, прилагательные, цвета} из «генератора уникальных имен»;

const ImprovedAdjectives = [
  ... прилагательные,
  'абразивный',
  'дерзкий',
  'черствый',
  'глупый',
  "эксцентричный",
];
const xMen = [
'ProfessorX',
'зверь',
'колосс',
'циклоп',
'ледяной человек',
'Росомаха',
];

const characterName: string = uniqueNamesGenerator ({
  словари: [SuperiorAdjectives, color, xMen],
  длина: 2,
  разделитель: '-'
}); // эксцентричный синий ледяной человек 

Руководство по миграции

Руководство по переходу с версии 3 на версию 4

Генератор уникальных имен v4 реализует новое критическое изменение.

Обязательно

словари конфиг

Теперь вы должны явно предоставить библиотеке используемые словари.
Это сделано для повышения гибкости и возможности встряхивания дерева для удаления неиспользуемых словарей из
размер вашего пакета.

Подробнее о словарях в разделе Словари.

версия 3

 импортировать {uniqueNamesGenerator} из генератора уникальных имен;

const randomName = uniqueNamesGenerator (); // big_red_donkey 

версия 4

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные, цвета, животные]
}

const randomName = uniqueNamesGenerator (конфигурация); // big_red_donkey 

Руководство по миграции с версии 1 или 2

В генераторе уникальных имен v3 реализовано несколько критических изменений.Если вы обновляете свою библиотеку с версии 1 или 2, вам может быть интересно узнать следующее:

uniqueNamesGenerator

Теперь это будет работать только тогда, когда предоставляется массив словарей в соответствии с
v4 критическое изменение.

версия 2

 импортировать {uniqueNamesGenerator} из генератора уникальных имен;

const randomName = uniqueNamesGenerator (); 

версия 4

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные, цвета, животные]
}

const randomName = uniqueNamesGenerator (конфигурация); // big_red_donkey 
Сепаратор

версия 2

 импортировать {uniqueNamesGenerator} из генератора уникальных имен;

const shortName = uniqueNamesGenerator ('-'); // большой красный осел 

версия 4

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные, цвета, животные],
  разделитель: '-'
}

const shortName = uniqueNamesGenerator (конфигурация); // большой красный осел 
Короткий

Свойство short было заменено на length , поэтому вы можете указать любое количество слов

версия 2

 импортировать {uniqueNamesGenerator} из генератора уникальных имен;

const shortName = uniqueNamesGenerator (истина); // большой осел 

версия 4

 импортировать {uniqueNamesGenerator, Config, прилагательные, цвета, животных} из «генератора уникальных имен»;

const config: Config = {
  словари: [прилагательные, цвета, животные],
  длина: 2
}

const shortName = uniqueNamesGenerator (конфигурация); // большой осел 

Вклад

Пожалуйста, прочтите ДОПОЛНИТЕЛЬНО.md, чтобы получить подробную информацию о нашем кодексе поведения и о процессе отправки нам запросов на вытягивание.

  1. Вилка!
  2. Создайте свою функциональную ветку: git checkout -b my-new-feature
  3. Добавьте свои изменения: git add.
  4. Зафиксируйте изменения: git commit -am 'Добавить функцию'
  5. Отправить в ветку: git push origin my-new-feature
  6. Отправить запрос на извлечение 😎

Лицензия

Лицензия MIT © Андреа Сонни

Авторы

Спасибо этим замечательным людям (смайлик):

Этот проект соответствует спецификации всех участников.Любые пожертвования приветствуются!

Список, набор, словарные выражения в Python | Индхумати Челлия | Аналитика Видхья

Давайте узнаем о списках, словарях, наборах в Python.

Фото Кара Идс на Unsplash

Понимания в Python:

Понимание состоит из одного выражения, за которым следует по крайней мере одно для предложения и ноль или более для предложений или для предложений .

В Python есть три понимания.

Типы интерпретаций в Python (Источник изображения: Автор)

Составные части списков:

Понятия списков обеспечивают краткий способ создания списков. Распространенными приложениями являются создание новых списков, в которых каждый элемент является результатом некоторых операций, применяемых к каждому члену другой последовательности или повторяемости, или создание подпоследовательности тех элементов, которые удовлетворяют определенному условию. — python docs

Синтаксис:

[выражение для элемента в итерируемом, если условное]

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

Это эквивалентно

  для элемента   в итерации : 
, если условное:
выражение

Тип возврата:

Список

Использование понимания списка:

Понимание списка состоит из скобок [] , содержащих выражение, за которым следует для предложения , затем ноль или более для или для предложений . Результатом будет новый список, полученный в результате оценки выражения в контексте для и , если за ним следуют предложения .

Объяснение понимания списка (Источник изображения: автор)

1. Понимание списка и цикл for.

При использовании функции «Понимание списка» он более лаконичен и удобочитаем по сравнению с циклом for.

Нахождение квадрата чисел с использованием понимания списка и цикла for:

цикла for vs понимание списка

2. Понимание списка vs фильтр.

filter:

Возвращает итератор из тех элементов iterable , для которых функция возвращает true. итерация может быть последовательностью, контейнером, поддерживающим итерацию, или итератором.

Поиск четных чисел с использованием понимания списка и filter ():

Анализ списка и фильтр ()

3. Понимание списка и карта.

map:

Возвращает итератор, который применяет функцию к каждому элементу итерируемого , давая результаты.

Нахождение квадрата чисел с помощью понимания списка и карты ():

Понимание списка и карты ()

4.Вложенные циклы в понимании списков.

Составление списка может содержать одно или несколько предложений for.

Пример 1 : Сгладить список, используя понимание списка с двумя предложениями «for»:

Сгладить список, используя понимание списка

5. Условие множественного if в понимании списка.

Составление списка может содержать ноль или более предложений if.

Пример: Нахождение чисел, которые делятся на 2 и 3.

Составление списка с использованием двух условий if

6.Выражение может быть кортежем в представлении списка.

Мы можем упомянуть выражение как кортеж в понимании списка.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *