Многие из нас даже и не знают что стандартная библиотека Python имеет ряд достаточно интересных и полезных модулей. Дабы не городить велосипеды в своих приложениях предлагаю ознакомиться с переводом статьи "
Модуль Collections
Модуль Collections реализует высокопроизводительные контейнерные типы данных. В настоящее время поддерживается четыре типа данных, - Counter, deque, OrderedDict и defaultdict, а также функция для создания типов данных - namedtuple().
Специализированные контейнеры, создаваемые при помощи этого модуля, представляют собой альтернативу встроенным в Python контейнерам общего назначения: dict, list, set и tuple.
Кроме контейнеров, модуль Collections содержит абстрактные базовые классы (ABC-классы, от "abstract base classes"), которые можно использовать для проверки классов на предмет наличия того или иного интерфейса, например, поддерживает ли он хеширование или "маппинг" -отображение.
Удобные и быстрые подсчеты обеспечит словарь Counter. Например:
>>> # Подсчет встречаемости слов в списке
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
... cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
>>> # Найти десять самых употребительных слов в "Гамлете"
>>> import re
>>> words = re.findall('\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
deque([iterable[, maxlen]])
Возвращает новый дэк-объект (двусвязную очередь), инициализированный при помощи append() на направление слева-направо и содержащий данные из iterable. Если аргумент iterable не задан, новый дэк будет создан пустым.
Дэки представляют собой объединение стеков и очередей (название произносится "deck" - дэк", это сокращение от "double-ended queue" - "двусторонняя очередь"). Дэки обеспечивают добавление и "выталкивание" элементов с обеих сторон, причем производительность операций ввода-вывода в обоих направлениях примерно одинакова, а память и механизм потоков используются при этом эффективно.
Хотя объекты list поддерживают сходный набор операций, они оптимизированы для быстрого выполнения операций с данными фиксированной длины и вызывают O(n) операций в памяти при выполнении pop(0) и insert(0, v) - для операций, которые меняют, одновременно, и размер, и положение представляемых данных.
Новое в версии 2.4.
Если параметр maxlen не определен или установлен в "None", дэк может вырастать до произвольных размеров. В противном случае он ограничен заданным предельным значением. Когда предельная длина дэка достигнута, при добавлении новых данных соответствующее количество элементов на противоположном конце очереди будет отброшено. Дэки с ограничением длины выполняют функцию, сходную с использованием фильтра tail в Unix. Также они полезны для отслеживания транзакций или других множеств данных, где существенны только самые последние операции:
def moving_average(iterable, n=3):
# скользящая средняя ([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
# См. http://ru.wikipedia.org/wiki/Скользящая_средняя
it = iter(iterable)
d = deque(itertools.islice(it, n-1))
d.appendleft(0)
s = sum(d)
for elem in it:
s += elem - d.popleft()
d.append(elem)
yield s / float(n)
DefaultDict
Использование list в качестве default_factory позволяет легко сгруппировать последовательность пар "ключ-значение" в словарь списков:
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Именованный кортеж - Namedtuple
В именованных кортежах каждому элементу кортежа присваивается имя, что делает код легко читаемым и самодокументирующимся. Их можно использовать точно так же, как и обычные кортежи, но при этом в них можно обращаться к полям по имени, а не только по индексу.
>>> from collections import namedtuple
>>> Point = namedtuple('Point', 'x y', verbose=True)
Упорядоченный словарь
Упорядоченные словари во всем похожи на обычные, но они запоминают тот порядок, в котором в них добавлялись их элементы. При проходе по упорядоченному словарю элементы будут возвращаться в том порядке, в котором первоначально в словарь добавлялись их ключи.
Модуль Itertools
В модуле itertools содержится ряд широко используемых итераторов, а также функции, позволяющие скомбинировать несколько итераторов. В этом разделе будут представлены на небольших примерах содержащиеся в этом модуле возможности. Функции этого модуля разделяются на четыре широких класса:
- Функции, создающие новый итератор на основе итератора существующего.
- Функции, которые принимают элементы итераторов в качестве своих аргументов.
- Функции для выделения частей в выводе итератора.
- Функция для группирования вывода итератора.
Создание новых итераторов
itertools.count(n) возвращает бесконечный поток целых чисел, увеличивающихся каждый раз на 1. При необходимости можно задать стартовое значение, которое по умолчанию равно 0:
itertools.count() =>
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
itertools.count(10) =>
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
itertools.cycle(iter) сохраняет копию содержимого итерируемого объекта и создает новый итератор, который возвращает все элементы от первого до последнего. Новый итератор будет повторять эту последовательность бесконечно.
itertools.cycle([1,2,3,4,5]) =>
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
itertools.repeat(elem, [n]) возвращает заданный элемент n раз, или возвращает этот элемент бесконечно, если параметр n не задан.
itertools.repeat('abc') =>
abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ...
itertools.repeat('abc', 5) =>
abc, abc, abc, abc, abc
itertools.chain(iterA, iterB, ...) получает произвольное количество итераторов на входе, и возвращает все элементы первого итератора, потом все элементы второго и так далее, пока содержимое итераторов не будет исчерпано.
itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>
a, b, c, 1, 2, 3
itertools.izip(iterA, iterB, ...) получает по одному элементу от каждого итерируемого объекта и возвращает их в форме кортежа:
itertools.izip(['a', 'b', 'c'], (1, 2, 3)) =>
('a', 1), ('b', 2), ('c', 3)
Это похоже на встроенную функцию zip(), но данная функция не конструирует в памяти полный список и не дожидается исчерпания всех входящих итераторов, прежде чем вернуть значение; вместо этого кортежи конструируются и возвращаются только по запросу (такое поведение технически обозначается термином
Этот итератор должен использоваться с итерируемыми объектами одинаковой длины. Если длина у них разная, результирующий поток будет иметь длину самого короткого из итерируемых множеств.
itertools.izip(['a', 'b'], (1, 2, 3)) =>
('a', 1), ('b', 2)
Этого следует избегать, поскольку полученный от более длинного итератора элемент может оказаться отброшенным. Это значит, что вы не сможете использовать далее эти итераторы из-за риска пропустить отброшенный элемент.
itertools.islice(iter, [start], stop, [step]) возвращает поток, представляющий собой срез итератора. С единственным аргументом stop функция вернет первые stop элементов. Если вы зададите стартовое значение, то получите stop-start элементов, а при задании шага step, элементы будут браться с соответствующим шагом. В отличие от срезов в строках и списках Python, здесь нельзя задавать отрицательные значения start, stop и step.
itertools.islice(range(10), 8) =>
0, 1, 2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8) =>
2, 3, 4, 5, 6, 7
itertools.islice(range(10), 2, 8, 2) =>
2, 4, 6
itertools.tee(iter, [n]) размножает итератор; функция возвращает n независимых итераторов (по умолчанию 2). Размножение итераторов требует сохранения части содержимого исходного итератора, поскольку процесс может потребовать значительного расхода памяти, если исходный итератор обладает большим объемом и один из новых итераторов потребит больше памяти, чем остальные.
itertools.tee( itertools.count() ) =>
iterA, iterB
where iterA ->
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
and iterB ->
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
Вызов функций для элементов
Две функции используются для вызова других функций в отношении элементов итерируемого объекта.
itertools.imap(f, iterA, iterB, ...) возвращает поток, содержащий f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]),...: itertools.imap(operator.add, [5, 6, 5], [1, 2, 3]) => 6, 8, 8Модуль operator содержит набор функций, соответствующих операторам Python. Вот некоторые примеры: operator.add(a, b) (суммирует два значения), operator.ne(a, b) (то же, что и a!=b), и operator.attrgetter('id') (возвращает ссылку, по которой можно получить атрибут "id" ).
itertools.starmap(func, iter) исходит из того, что итерируемый объект вернет поток кортежей, и вызывает функцию f() с полученными кортежами в качестве аргументов:
itertools.starmap(os.path.join,
[('/usr', 'bin', 'java'), ('/bin', 'python'),
('/usr', 'bin', 'perl'),('/usr', 'bin', 'ruby')])
=>
/usr/bin/java, /bin/python, /usr/bin/perl, /usr/bin/ruby
Выделение подмножеств элементов
Еще одна группа функций служит для выделения подмножеств элементов в итераторе на основе предиката.
itertools.ifilter(predicate, iter) возвращает все элементы, для которых предикат оказывается истинным:
def is_even(x):
return (x % 2) == 0
itertools.ifilter(is_even, itertools.count()) =>
0, 2, 4, 6, 8, 10, 12, 14, ...
itertools.ifilterfalse(predicate, iter) - противоположная функция, возвращает все элементы, для которых предикат оказывается ложным:
itertools.ifilterfalse(is_even, itertools.count()) =>
1, 3, 5, 7, 9, 11, 13, 15, ...
itertools.takewhile(predicate, iter) возвращает элементы до тех пор, пока предикат истинен. Как только предикат становится ложным, итератор сообщает об исчерпании своих результатов.
def less_than_10(x):
return (x
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
itertools.takewhile(is_even, itertools.count()) =>
0
itertools.dropwhile(predicate, iter) отбрасывает элементы до тех пор, пока предикат остается истинным, а когда предикат становится ложным, возвращает все оставшиеся элементы.
itertools.dropwhile(less_than_10, itertools.count()) =>
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...
itertools.dropwhile(is_even, itertools.count()) =>
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
Группирование элементов
Последняя функция из тех, которые я хотел обсудить, это itertools.groupby(iter, key_func=None), - она наиболее сложна. Здесь key_func(elem) - функция, вычисляющая значение ключа для каждого элемента, возвращаемого итератором. Если вы не задаете ключа, то ключом будет просто сам элемент.
groupby() собирает все последовательные элементы из заданного итерируемого множества, у которых совпадает значение ключа, и возвращает поток кортежей, в каждом из которых содержится значение ключа и итератор для получения элементов с этим значением ключа.
city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),
('Anchorage', 'AK'), ('Nome', 'AK'),
('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),
...
]
def get_state ((city, state)):
return state
itertools.groupby(city_list, get_state) =>
('AL', iterator-1),
('AK', iterator-2),
('AZ', iterator-3), ...
where
iterator-1 =>
('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL')
iterator-2 =>
('Anchorage', 'AK'), ('Nome', 'AK')
iterator-3 =>
('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ')
groupby() исходит из предположения, что содержимое итерируемого множества уже отсортировано по значению ключа. Обратите внимание - возвращаемые функцией итераторы опираются на то же самое итерируемое множество, поэтому нужно вначале выбрать все значения из итератора-1, прежде чем переходить к итератору-2 и его ключу.