Красота и изящность стандартной библиотеки Python
-
Upload
python-meetup -
Category
Documents
-
view
3.293 -
download
0
description
Transcript of Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
Примеры использования
Макс Усачев[email protected]
Про группировкуэлементов
Группировка элементов
Дана произвольная строка
import string import random
RANDOM_STRING_LENGTH = 999 choices = string.ascii_letters + string.digits
random_string = [random.choice(choices) for \ i in xrange(RANDOM_STRING_LENGTH)]
Найти позиции каждого уникального символа этой строки в исходной строке.
Группировка элементов
Первое, что приходит людям на уми они так это и оставляют:
result = {} for index in xrange(len(random_string)): element = random_string[index] if element in result.keys(): result[element].append(index) else: result[element] = [index]
Группировка элементов
После тревожной ночи иногдакод становится чуть лучше:
result = {} for index, element in enumerate(random_string): if element in result: result[element].append(index) else: result[element] = [index]
Группировка элементов
А потом ты понимаешь...Что кода слишком много!
result = {} for index, element in enumerate(random_string): result.setdefault(element, []).append(index)
Группировка элементов
Казалось бы, куда уже короче ...
result = {} for index, element in enumerate(random_string): result.setdefault(element, []).append(index)
Группировка элементов
Модуль collections !
Модуль collections реализует высокопроизводительные контейнерные типы данных.
Counter, deque, OrderedDict и defaultdict, а также функция для создания типов данных - namedtuple().
Контейнеры, создаваемые при помощи этого модуля, представляют собой альтернативу встроенным в Python контейнерам общего назначения: dict, list, set и tuple.
Документация: http://docs.python.org/2/library/collections.html#module-collectionsИсходный код: http://hg.python.org/cpython/file/2.7/Lib/collections.py
http://hg.python.org/cpython/file/2.7/Lib/_abcoll.py
Группировка элементов
Примеры использования defaultdictКогда фабричная функция is None
>>> from collections import defaultdict
>>> d = defaultdict() >>> d['foo'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'foo'
new in Python v2.5
Если фабричная функция не указана, при обращении по несуществующему ключу ведет себя как обычный dict.
Группировка элементов
Примеры использования defaultdictКогда фабричная функция is not None
>>> d = defaultdict(int) >>> d['foo'] 0 >>> d['foo'] += 1 >>> d defaultdict(<type 'int'>, {'foo': 1}) >>> d['foo1'] += 5 >>> d defaultdict(<type 'int'>, {'foo': 1, 'foo1': 5})
Группировка элементов
Примеры использования defaultdictКогда фабричная функция is not None
>>> d = defaultdict(list) >>> d['foo'].append('foo') >>> d defaultdict(<type 'list'>, {'foo': ['foo']}) >>> d = defaultdict(set) >>> d['foo'].add('foo') >>> d['foo'].add('bar') >>> d['foo'].add('foo') >>> d defaultdict(<type 'set'>, {'foo': set(['foo', 'bar'])}
Группировка элементов
Теперь все ясно ;)
result = {} for index, element in enumerate(random_string): result.setdefault(element, []).append(index)
Используем defaultdict
result = defaultdict(list) for index, element in enumerate(random_string): result[element].append(index)
Группировка элементов
Действительно, высокопроизводительный контейнерРезультаты timeit
Самый простой подход, (if key in result ...): 3.07053208351
С использованием setdefault: 3.91122484207
С использованием defaultdict: 2.63944602013
Про именование
Назови меня по имени
Знакомая картина?
# pure Python elements = [ (subelement1, subelement2, ....), (subelement11, subelement22, ....), (subelement111, subelement222, ....), (subelement1111, subelement2222, ....), ]
# Django Model.objects.all().values_list('name', 'surname', 'age')
Назови меня по имени
Более "живой" пример
from mock import Mock
database = Mock() database.make_query = Mock(return_value=[
('Book1', 'Author1', '10/01/2001'),('Book2', 'Author2', '10/02/2002'),('Book3', 'Author3', '10/03/2003'),
('Book4', 'Author4', '10/04/2004'),('Book5', 'Author5', '10/05/2005')])
publications = data.make_query() # pass publications as template context
Назови меня по имени
Дальше только хуже ...
{% for publication in publications %} <div>publication[0]</div> <span>publication[1]</span> <b>publication[2]</b> {% endfor %}
Назови меня по имени
Модуль collections!
namedtuple - фабричная функция для кортежей с именованными полями.
Именованные кортежи позволяют определить имена для каждой позиции в кортеже.
Использование именованных кортежей позволяет создавать более читаемый и понятный код. Они могут быть использованы в тех же случаях, что и обычные кортежи, а обращаться к полям можно не только по индексу, но и по имени.
Именованные кортежи используют памяти не больше, чем обычные кортежи.
new in Python v2.6
Назови меня по имени
Как оно работает
>>> from collections import namedtuple
>>> Person = namedtuple('Person', 'name surname age') >>> person = Person(name='Max', surname='Usachev', age=25) >>> person = Person('Max', 'Usachev', 25)
>>> person.name, person.surname, person.age ('Max', 'Usachev', 25) >>> person[0], person[1], person[2] ('Max', 'Usachev', 25)
Назови меня по имени
Как оно работает еще
>>> person._asdict() OrderedDict([('name', 'Max'), ('surname', 'Usachev'), ('age', 25)])
>>> Person.full_name = property(lambda self: ' '.join((self.name, self.surname))) >>> person.full_name 'Max Usachev'
>>> Person._make(('Max', 'Usachev', 25)) Person(name='Max', surname='Usachev', age=25) >>> Person._fields ('name', 'surname', 'age')
Назови меня по имени
Дополненный начальный пример
from collections import namedtuple ...
Publication = namedtuple('Publication', 'name author date') publications = map(Publication._make, database.make_query())
{% for publication in publications %} <div>publication.name</div> <span>publication.author</span> <b>publication.date</b> {% endfor %}
Про сортировку
sortируй правильно
Все та же randomная строкаЗнаем что и как часто
# DON'T COUNT THIS WAY
letters = [(letter, random_string.count(letter)) for \ letter in set(random_string)]
Какой символ встречается чаще всего?
sortируй правильно
Самое распространенное решение
sorted(letters, key=lambda element: element[1])
sortируй правильно
Но все-таки лучше так:
sorted(letters, key=itemgetter(1))
И timeit уверен в этом!
С использованием lambda: 4.4638299942
С использованием itemgetter: 3.61604499817
sortируй правильно
Модуль operator
Модуль operator предлагает набор эффективных функций, соответствующих внутренним операторам Python:- add(a,b)- div(a, b)- not(a)- ...
А также такие полезные функции, как:- attrgetter(*attrs)- itemgetter(*items)- methodcaller(name[, args...]
sortируй правильно
Как работает operator.itemgetter
>>> from operator import itemgetter
>>> getter = itemgetter(0) >>> getter('abc') 'a' >>> getter([0, 1, 2]) 0
>>> getter = itemgetter(0, -1) >>> getter('abc') ('a', 'c') >>> getter([1, 2, 3]) (1, 3)
new in Python v2.4
sortируй правильно
Как работает operator.attrgetter
>>> from operator import attrgetter
>>> getter = attrgetter('name') >>> class Foo(object): pass >>> foo = Foo() >>> foo.name = 'foo' >>> getter(foo) 'foo' >>> foo.weight = 20 >>> getter = attrgetter('name', 'weight') >>> getter(foo) ('foo', 20)
new in Python v2.4
Про фильтрацию
filtруй правильно
Часто нужно принять решение на основе содержимого списка,а точнее, проверить, не пустой ли он
elements = [...] condition = lambda x: ...
if len([element for element in elements if condition(element)]): # do something
filtруй правильно
Для начала так:
if [element for element in elements if condition(element)]: # do something
* Но список может быть большим
* А если нашли первый подходящий элемент, то зачем искать дальше?
Напрашивается решение на основе генераторов!
filtруй правильно
Вот оно!
if any(element for element in elements if condition(element)): # do something
Т.е. any вернет True как только встретит первый трушный элемент
Внимание, вопрос: что тут не так?
filtруй правильно
Как работает any:
def any(iterable): for element in iterable: if element: return True return False
filtруй правильно
И если вдруг:
>>> nums = [0, 22, 15] >>> condition = lambda x: x < 10 >>> any(num for num in nums if condition(num)) False
filtруй правильно
Решение:
>>> any(condition(num) for num in nums) True
делай все правильно
* Пишите красивый код
* Пишите правильный код (cпрашивайте себя "Не ХХХXХ ерунду ли я пишу?")
* Изучайте стандартную библиотеку Python
* Используйте collections, operator, itertools ...
* Приходите снова!