На Хабре уже есть несколько статей\переводов, в которых рассказывается о неизвестных фичах\тонкостях\возможностях Пайтона. Я буду пытаться не повторять их, а дополнять, но если уж так случилось, что вы это уже где-то видели — не огорчайтесь. Я уверен, что найдется что-то интересное и для вас.
Итак, поехали.
Правда и то, что вся магия поломается если добавить скобки:
Дока
Built-in функция iter возвращает итератор для переданной последовательности. Но, если передавать два параметра, то первый должен быть callable-объектом, а второй — результатом вызова первого объекта, при котором нужно прекратить итерацию. Например, читаем из файла до первой пустой строки:
Дока
Позволяет легко и красиво пользоваться синтаксисом with EXPR as VAR для своих собственных объектов, функций и т.д. Пример из документации:
Дока, PEP 343
Здесь несколько «хаков». Во первых, использование изменяемых (mutable) объектов в качестве аргументов по умолчанию — почти всегда плохая идея:
Почему так? Значения агрументов по умолчанию определяются только раз при создании функции и сохраняются в свойстве func_defaults:
Но есть в довесок и полезный рецепт. Следующий код работает не совсем так, как ожидается:
Поправить его можно просто и элегантно — достаточно заменить lambda: i * 2 на lambda i=i: i * 2:
О именах и привязке к ним можно почитать в Execution Model.
В зависимости от контекста ... (три точки) может быть допустимым синтаксисом. Так, list[...] передает в функцию __getitem__ объект типа Ellipsis — единственный и неповторимый в своем роде. Демонстрация:
Используется эта прелесть в Numpy.
В 10-ом классе нам на уроке информатике предлагали задачку — обменять местами значения двоих переменных, не используя третью. В Пайтоне это даже не вопрос:
В list comprehensions можно использовать несколько for:
Например, надо создать список из 5 списков. Хочется сделать так:
Но нельзя:
Лучше наверное вот так:
Супер секретный метод шифрования Rot13:
Далее. Например, есть строка из внешнего источника, при этом в строке есть литералы \n, \t, \r и т.п. Как получить отформатированную строку (по сути, сделать безопасный eval)?
unicode_escape работает аналогично, только с юникодом, а не со строками. Так же он умеет превратить строку '\u0457' в букву "ї":
Перечень поддерживаемых кодировок — standard-encodings.
Очень полезная либа для работы с текстами. Делаем с длинной строки много мелких:
Дока — там ещё много вкусного.
Сплошное умиления. Читать-не перечитать, особенно раздел с рецептами. Здесь вспомню grouper:
фишка в том, что мы работаем с одним итератором
Большая часть информации из замечательного топика на StackOverFlow, остальное — собственные наблюдения. На эту тему можно писать много, поэтому старался поделится только самым интересным и полезным.
Спасибо за внимание!
Да прибудет с вами сила документации!
P.S. Если есть замечания — буду очень признателен за сообщения в личку.
Итак, поехали.
Цепочки операторов сравнения (chaining comparison):
>>> 1 < 5 < 10
True
>>> 1 < 11 < 10
False
>>> 1 < 11 > 10
True
Правда и то, что вся магия поломается если добавить скобки:
>>> (1 < 11) > 10
False
Дока
iter и два параметра
Built-in функция iter возвращает итератор для переданной последовательности. Но, если передавать два параметра, то первый должен быть callable-объектом, а второй — результатом вызова первого объекта, при котором нужно прекратить итерацию. Например, читаем из файла до первой пустой строки:
with open('mydata.txt') as fp:
for line in iter(fp.readline, ''):
process_line(line)
Дока
contextlib
Позволяет легко и красиво пользоваться синтаксисом with EXPR as VAR для своих собственных объектов, функций и т.д. Пример из документации:
from contextlib import contextmanager
@contextmanager
def tag(name):
print "[%s]" % name
yield
print "[/%s]" % name
>>> with tag("h1"):
... print "foo"
...
[h1]
foo
[/h1]
Дока, PEP 343
Аргументы по умолчанию
Здесь несколько «хаков». Во первых, использование изменяемых (mutable) объектов в качестве аргументов по умолчанию — почти всегда плохая идея:
>>> def foo(a=list()):
... a.append(1)
... print a
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]
Почему так? Значения агрументов по умолчанию определяются только раз при создании функции и сохраняются в свойстве func_defaults:
>>> foo.func_defaults
([1, 1, 1],)
Но есть в довесок и полезный рецепт. Следующий код работает не совсем так, как ожидается:
>>> l = []
>>> for i in range(3):
... l.append(lambda: i * 2)
...
>>> for x in l:
... x()
...
4
4
4
Поправить его можно просто и элегантно — достаточно заменить lambda: i * 2 на lambda i=i: i * 2:
>>> l = []
>>> for i in range(3):
... l.append(lambda i=i: i * 2)
...
>>> for x in l:
... x()
...
0
2
4
О именах и привязке к ним можно почитать в Execution Model.
Ellipsis
В зависимости от контекста ... (три точки) может быть допустимым синтаксисом. Так, list[...] передает в функцию __getitem__ объект типа Ellipsis — единственный и неповторимый в своем роде. Демонстрация:
>>> class L(list):
... def __getitem__(self, *args):
... print args
... return list.__getitem__(self, *args)
...
>>> l[...]
(Ellipsis,)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getitem__
TypeError: list indices must be integers, not ellipsis
Используется эта прелесть в Numpy.
Обмен значениями переменных
В 10-ом классе нам на уроке информатике предлагали задачку — обменять местами значения двоих переменных, не используя третью. В Пайтоне это даже не вопрос:
>>> a = 10
>>> b = 5
>>> a, b
(10, 5)
>>> a, b = b, a
>>> a, b
(5, 10)
Вложенные list comprehensions
В list comprehensions можно использовать несколько for:
>>> l = [[1,2,3], [4,5,6]]
>>> [lll * 2 for ll in l for lll in ll]
[2, 4, 6, 8, 10, 12]
Создание нескольких изменяемых объектов
Например, надо создать список из 5 списков. Хочется сделать так:
>>> l = [[]] * 5
>>> l
[[], [], [], [], []]
Но нельзя:
>>> l[0].append(1)
>>> l
[[1], [1], [1], [1], [1]]
Лучше наверное вот так:
>>> l = [[] for _ in range(5)]
rot13, string_escape, unicode_escape кодировки
Супер секретный метод шифрования Rot13:
>>> 'Hello world!'.encode('rot13')
'Uryyb jbeyq!'
>>> 'Uryyb jbeyq!'.decode('rot13')
u'Hello world!'
Далее. Например, есть строка из внешнего источника, при этом в строке есть литералы \n, \t, \r и т.п. Как получить отформатированную строку (по сути, сделать безопасный eval)?
>>> s = 'Hello\\n\\rworld!'
>>> s
'Hello\\n\\rworld!'
>>> repr(s)
"'Hello\\\\n\\\\rworld!'"
>>> print s.decode('string_escape')
Hello
world!
unicode_escape работает аналогично, только с юникодом, а не со строками. Так же он умеет превратить строку '\u0457' в букву "ї":
>>> print '\u0457'.decode('unicode_escape')
ї
Перечень поддерживаемых кодировок — standard-encodings.
textwrap
Очень полезная либа для работы с текстами. Делаем с длинной строки много мелких:
>>> s = "Python is a programming language that lets you work more quickly and integrate your systems more effectively. You can learn to use Python and see almost immediate gains in productivity and lower maintenance costs."
>>> print textwrap.fill(s, 25)
Python is a programming
language that lets you
work more quickly and
integrate your systems
more effectively. You can
learn to use Python and
see almost immediate
gains in productivity and
lower maintenance costs.
Дока — там ещё много вкусного.
itertools
Сплошное умиления. Читать-не перечитать, особенно раздел с рецептами. Здесь вспомню grouper:
>>> def grouper(n, iterable, fillvalue=None):
... "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
... args = [iter(iterable)] * n
... return izip_longest(fillvalue=fillvalue, *args)
...
>>> list(grouper(2, [1,2,3,4,5,6]))
[(1, 2), (3, 4), (5, 6)]
>>> list(grouper(3, [1,2,3,4,5,6]))
[(1, 2, 3), (4, 5, 6)]
>>> list(grouper(4, [1,2,3,4,5,6], fillvalue=0))
[(1, 2, 3, 4), (5, 6, 0, 0)]
фишка в том, что мы работаем с одним итератором
Заключения
Большая часть информации из замечательного топика на StackOverFlow, остальное — собственные наблюдения. На эту тему можно писать много, поэтому старался поделится только самым интересным и полезным.
Спасибо за внимание!
Да прибудет с вами сила документации!
P.S. Если есть замечания — буду очень признателен за сообщения в личку.