Pull to refresh

Python-неизвестный

Reading time4 min
Views27K
На Хабре уже есть несколько статей\переводов, в которых рассказывается о неизвестных фичах\тонкостях\возможностях Пайтона. Я буду пытаться не повторять их, а дополнять, но если уж так случилось, что вы это уже где-то видели — не огорчайтесь. Я уверен, что найдется что-то интересное и для вас.

Итак, поехали.

Цепочки операторов сравнения (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. Если есть замечания — буду очень признателен за сообщения в личку.
Tags:
Hubs:
+136
Comments51

Articles

Change theme settings