Pull to refresh

Основы Python — кратко. Часть 4. Генераторы списков

Reading time 3 min
Views 217K
List comprehensions

Продолжим наш цикл уроков. Добрый день.

Генерация списков

Генерация списков (не знаю как адекватно перевести на русский list comprehensions) — яркий пример «синтаксического сахара». То есть конструкции, без которой легко можно обойтись, но с ней намного лучше :) Генераторы списков, как это не странно, предназначены для удобной обработки списков, к которой можно отнести и создание новых списков, и модификацию существующих.
Допустим, нам необходимо получить список нечетных чисел, не превышающих 25.
В принципе, только познакомившись с работой команды xrange решить эту проблему несложно.

>>> res = []
>>> for x in xrange(1, 25, 2):
...     res.append(x)
...
>>> print res 

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

>>> res = [x for x in xrange(1, 25, 2)]
>>> print res
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]

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

>>> res = [x**2 for x in xrange(1, 25, 2)]
>>> print res
[1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529]

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

>>> res = [x**2 for x in xrange(1, 25, 2) if x % 3 != 0]
>>> print res
[1, 25, 49, 121, 169, 289, 361, 529]

Вот пример немного посложнее.

>>> dic = {'John':1200, 'Paul':1000, 'Jones':1850, 'Dorothy': 950}
>>> print "\n".join(["%s = %d" % (name, salary) for name, salary in dic.items()])
Jones = 1850
Dorothy = 950
Paul = 1000
John = 1200

Тут мы использовали генератор списков для превращения словаря в набор записей, это может быть удобно в таких задачах как сохранение конфигов или генерация HTML. Выражение "%s = %d" % (name, salary) — предназначено для форматирования строк, и по сути похоже на аналоги в С. В начале идет строка где задаются позиции для вставки значений (%s — строковое, %d — числовое). После знака % — «вставляемые» значения в виде кортежа.

В принципе — генератором можно обрабатывать и готовые списки.

Рассмотрим простой пример. Допустим, у нас имеется лог-файл, в котором хранится статистика запросов к серверу в виде «ip bytes» через пробел, по одному хосту на
строку. Примерно так:

127.0.0.1 120
10.1.1.1 210
127.0.0.1 80
10.1.1.1 215
10.1.1.1 200
10.1.1.2 210

Нам необходимо вычислить суммарный объем траффика на каждый хост и выдать его в виде списка в поряке убывания траффика.
Программу для решения данной проблемы будет весьма недлинной :) Ее можно еще сильнее сократить, но это явно пойдет в ущерб ее читабельности.

#!/usr/bin/env python
#coding: utf8

# 1 считываем из файла строки и делим их на пары IP-адрес
raw = [x.split(" ")  for x in open("log.txt")]

# 2 заполняем словарь
rmp = {}
for ip, traffic in raw:
        if ip in rmp:
                rmp[ip] += int(traffic)
        else:
                rmp[ip] = int(traffic)

# 3 переводим в список и сортируем
lst = rmp.items()
lst.sort(key = lambda (key, val): key)
# 4 получаем результат
print "\n".join(["%s\t%d" % (host, traff) for host, traff in lst])

Разберем ее пошагово.
1. В этой строке мы читаем из файла, который открываем с помощью функции open. Функция open по-умолчанию открывает файл для чтения и возвращает файловый объект, который кроме всего прочего является итерируемым. То есть по нему можно «двигаться» с помощью цикла for, чем мы и пользуемся в нашем случае. Кроме того, с помощью метода split мы делим каждую строку на пару — адрес — траффик.
2. Для удобства, мы формируем хеш, ключами в котором являются адреса, а значениями — траффик. Если ключа еще нет, то мы его созадаем, если же есть, то мы плюсуем текущий траффик к его предыдущим «наработкам». После этого цикла мы получим хеш с суммами траффика по всем хостам.
3. К сожалению, словари в Пайтоне не сортируются. Потому нам придется перевести его в список для сортировки. Следующие две строки переводят словарь в список и осуществляют его сортировку по второму полю.
4. Вот и все что осталось — «собрать» результат, как мы уже это делали раньше.

На сегодня хватит :)
Домашнее задание.
1. Реализовать функцию-генератор строки с таблицей умножения на число Х.
2. Есть лог-файл какого-то чата. Посчитать «разговорчивость» пользователей в нем в виде ник — количество фраз. Посчитать среднее число букв на участника чата.

ЗЫ О чем лучше продолжать — про классы и ООП или про элементы функционального программирования?
Tags:
Hubs:
+1
Comments 83
Comments Comments 83

Articles