Скрытые возможности Python

Каковы менее известные, но полезные функции языка программирования Python?

  • Попробуйте ограничить ответы ядром Python.
  • Одна особенность за ответ.
  • Приведите пример и краткое описание функции, а не просто ссылку на документацию.
  • Пометьте объект, используя заголовок в качестве первой строки.

Быстрые ссылки на ответы:

191 ответ

Операторы сравнения цепочек:

>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20 
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True

Если вы думаете, что это делает 1 < x, который выходит как True, а затем сравнивая True < 10что также True, тогда нет, это действительно не то, что происходит (см. последний пример). Это действительно переводит на 1 < x and x < 10, а также x < 10 and 10 < x * 10 and x*10 < 100, но с меньшим набором текста и каждый термин оценивается только один раз.

Получите дерево разбора регулярных выражений python для отладки вашего регулярного выражения.

Регулярные выражения - отличная особенность python, но их отладка может быть проблемой, и слишком легко ошибиться в регулярных выражениях.

К счастью, python может распечатать дерево разбора регулярных выражений, передав недокументированный экспериментальный скрытый флаг re.DEBUG (на самом деле, 128) re.compile,

>>> re.compile("^\[font(?:=(?P<size>[-+][0-9]{1,2}))?\](.*?)[/font]",
    re.DEBUG)
at at_beginning
literal 91
literal 102
literal 111
literal 110
literal 116
max_repeat 0 1
  subpattern None
    literal 61
    subpattern 1
      in
        literal 45
        literal 43
      max_repeat 1 2
        in
          range (48, 57)
literal 93
subpattern 2
  min_repeat 0 65535
    any None
in
  literal 47
  literal 102
  literal 111
  literal 110
  literal 116

Как только вы поймете синтаксис, вы сможете обнаружить свои ошибки. Там мы видим, что я забыл сбежать [] в [/font],

Конечно, вы можете комбинировать его с любыми флагами, например, с закомментированными регулярными выражениями:

>>> re.compile("""
 ^              # start of a line
 \[font         # the font tag
 (?:=(?P<size>  # optional [font=+size]
 [-+][0-9]{1,2} # size specification
 ))?
 \]             # end of tag
 (.*?)          # text between the tags
 \[/font\]      # end of the tag
 """, re.DEBUG|re.VERBOSE|re.DOTALL)

перечислять

Оберните итерируемое с помощью enumerate, и оно выдаст элемент вместе с его индексом.

Например:


>>> a = ['a', 'b', 'c', 'd', 'e']
>>> for index, item in enumerate(a): print index, item
...
0 a
1 b
2 c
3 d
4 e
>>>

Рекомендации:

Создание генераторов объектов

Если ты пишешь

x=(n for n in foo if bar(n))

Вы можете достать генератор и назначить его на х. Теперь это означает, что вы можете сделать

for n in x:

Преимущество этого в том, что вам не нужно промежуточное хранилище, которое понадобилось бы, если бы вы сделали

x = [n for n in foo if bar(n)]

В некоторых случаях это может привести к значительному ускорению.

Вы можете добавить многие операторы if в конец генератора, в основном копируя вложенные циклы:

>>> n = ((a,b) for a in range(0,2) for b in range(4,6))
>>> for i in n:
...   print i 

(0, 4)
(0, 5)
(1, 4)
(1, 5)

iter() может принимать вызываемый аргумент

Например:

def seek_next_line(f):
    for c in iter(lambda: f.read(1),'\n'):
        pass

iter(callable, until_value) функция многократно вызывает callable и дает свой результат до until_value возвращается

Будьте осторожны с изменяемыми аргументами по умолчанию

>>> def foo(x=[]):
...     x.append(1)
...     print x
... 
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

Вместо этого вам следует использовать значение Sentinel, обозначающее "не дано", и заменить его изменяемым по умолчанию:

>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]

Отправка значений в функции генератора. Например, имея эту функцию:

def mygen():
    """Yield 5 until something else is passed back via send()"""
    a = 5
    while True:
        f = (yield a) #yield a and possibly get f in return
        if f is not None: 
            a = f  #store the new value

Вы можете:

>>> g = mygen()
>>> g.next()
5
>>> g.next()
5
>>> g.send(7)  #we send this back to the generator
7
>>> g.next() #now it will yield 7 until we send something else
7

Если вам не нравится использовать пробелы для обозначения областей, вы можете использовать стиль C {}, выполнив:

from __future__ import braces

Шаг аргумента в операторах среза. Например:

a = [1,2,3,4,5]
>>> a[::2]  # iterate over the whole list in 2-increments
[1,3,5]

Особый случай x[::-1] является полезной идиомой для "х обратный".

>>> a[::-1]
[5,4,3,2,1]

Декораторы

Декораторы позволяют обернуть функцию или метод в другую функцию, которая может добавить функциональность, изменить аргументы или результаты и т. Д. Декораторы пишутся на одну строку выше определения функции, начиная со знака "at" (@).

Пример показывает print_args декоратор, который печатает аргументы декорированной функции перед вызовом:

>>> def print_args(function):
>>>     def wrapper(*args, **kwargs):
>>>         print 'Arguments:', args, kwargs
>>>         return function(*args, **kwargs)
>>>     return wrapper

>>> @print_args
>>> def write(text):
>>>     print text

>>> write('foo')
Arguments: ('foo',) {}
foo

Синтаксис for...else (см. http://docs.python.org/ref/for.html)

for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")

Блок "else" будет обычно выполняться в конце цикла for, если не вызывается break.

Приведенный выше код можно эмулировать следующим образом:

found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found: 
    print("i was never 0")

Начиная с 2.5 года у диктов есть специальный метод __missing__ который вызывается для отсутствующих элементов:

>>> class MyDict(dict):
...  def __missing__(self, key):
...   self[key] = rv = []
...   return rv
... 
>>> m = MyDict()
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}

Существует также подкласс dict в collections называется defaultdict это делает то же самое, но вызывает функцию без аргументов для несуществующих элементов:

>>> from collections import defaultdict
>>> m = defaultdict(list)
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}

Я рекомендую преобразовывать такие диктовки в обычные, прежде чем передавать их функциям, которые не ожидают таких подклассов. Много кода использует d[a_key] и перехватывает KeyErrors, чтобы проверить, существует ли элемент, который добавит новый элемент к dict.

Обмен стоимости на месте

>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)

Правая часть присваивания является выражением, которое создает новый кортеж. Левая часть назначения немедленно распаковывает этот (не связанный) кортеж с именами a а также b,

После назначения новый кортеж не имеет ссылки и помечается для сборки мусора, а значения привязываются к a а также b были поменяны местами.

Как отмечено в разделе учебника Python по структурам данных,

Обратите внимание, что множественное назначение на самом деле является просто комбинацией упаковки кортежа и распаковки последовательности.

Читаемые регулярные выражения

В Python вы можете разбить регулярное выражение на несколько строк, назвать свои совпадения и вставить комментарии.

Пример подробного синтаксиса (из Dive в Python):

>>> pattern = """
... ^                   # beginning of string
... M{0,4}              # thousands - 0 to 4 M's
... (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                     #            or 500-800 (D, followed by 0 to 3 C's)
... (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                     #        or 50-80 (L, followed by 0 to 3 X's)
... (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                     #        or 5-8 (V, followed by 0 to 3 I's)
... $                   # end of string
... """
>>> re.search(pattern, 'M', re.VERBOSE)

Пример именования совпадений (из HOWTO Regular Expression)

>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'

Вы также можете подробно написать регулярное выражение без использования re.VERBOSE благодаря строковому буквальному конкатенации.

>>> pattern = (
...     "^"                 # beginning of string
...     "M{0,4}"            # thousands - 0 to 4 M's
...     "(CM|CD|D?C{0,3})"  # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                         #            or 500-800 (D, followed by 0 to 3 C's)
...     "(XC|XL|L?X{0,3})"  # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                         #        or 50-80 (L, followed by 0 to 3 X's)
...     "(IX|IV|V?I{0,3})"  # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                         #        or 5-8 (V, followed by 0 to 3 I's)
...     "$"                 # end of string
... )
>>> print pattern
"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"

Распаковка аргумента функции

Вы можете распаковать список или словарь в качестве аргументов функции, используя * а также **,

Например:

def draw_point(x, y):
    # do some magic

point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}

draw_point(*point_foo)
draw_point(**point_bar)

Очень полезный ярлык, поскольку списки, кортежи и тексты широко используются в качестве контейнеров.

ROT13 является допустимой кодировкой для исходного кода, когда вы используете правильное объявление кодировки в верхней части файла кода:

#!/usr/bin/env python
# -*- coding: rot13 -*-

cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")

Создание новых типов в полностью динамичной манере

>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"

что точно так же, как

>>> class NewType(object):
>>>     x = "hello"
>>> n = NewType()
>>> n.x
"hello"

Наверное, не самая полезная вещь, но приятно знать.

Редактировать: исправлено имя нового типа, должно быть NewType быть точно таким же, как с class заявление.

Редактировать: отрегулировал заголовок, чтобы более точно описать функцию.

Контекстные менеджеры иwith" Утверждение

Представленный в PEP 343, менеджер контекста- это объект, который действует как контекст времени выполнения для набора операторов.

Поскольку эта функция использует новые ключевые слова, она вводится постепенно: она доступна в Python 2.5 через__future__ директивы. В Python 2.6 и выше (включая Python 3) он доступен по умолчанию.

Я часто использовал выражение "с", потому что я думаю, что это очень полезная конструкция, вот небольшая демонстрация:

from __future__ import with_statement

with open('foo.txt', 'w') as f:
    f.write('hello!')

То, что происходит здесь за кулисами, это то, что выражение "с" вызывает особый __enter__ а также __exit__ методы на объекте файла. Детали исключения также передаются __exit__ если какое-либо исключение было вызвано из тела оператора with, что позволяет обрабатывать исключение там.

В данном конкретном случае это делает для вас то, что он гарантирует, что файл будет закрыт, когда выполнение выпадает из области действия with Suite, независимо от того, происходит ли это нормально или было ли выброшено исключение. Это в основном способ абстрагирования общего кода обработки исключений.

Другие общие случаи использования для этого включают блокировку с потоками и транзакциями базы данных.

В словарях есть метод get ()

В словарях есть метод get (). Если вы делаете d['key'], а key не существует, вы получаете исключение. Если вы сделаете d.get ("ключ"), вы получите "Нет", если "ключ" не существует. Вы можете добавить второй аргумент, чтобы вернуть этот элемент вместо None, например: d.get('key', 0).

Это здорово для таких вещей, как сложение чисел:

sum[value] = sum.get(value, 0) + 1

Дескрипторы

Они - магия целого ряда основных функций Python.

Когда вы используете точечный доступ для поиска элемента (например, xy), Python сначала ищет элемент в словаре экземпляра. Если он не найден, он ищет его в словаре классов. Если он находит его в словаре классов, и объект реализует протокол дескриптора, а не просто возвращает его, Python выполняет его. Дескриптор - это любой класс, который реализует __get__, __set__, или же __delete__ методы.

Вот как бы вы реализовали свою собственную (только для чтения) версию свойства, используя дескрипторы:

class Property(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, obj, type):
        if obj is None:
            return self
        return self.fget(obj)

и вы будете использовать его так же, как встроенное свойство ():

class MyClass(object):
    @Property
    def foo(self):
        return "Foo!"

Дескрипторы используются в Python для реализации свойств, привязанных методов, статических методов, методов класса и слотов, среди прочего. Понимание их позволяет легко понять, почему многие вещи, которые раньше выглядели как "причуды" Python, являются такими, какие они есть.

У Раймонда Хеттингера отличный учебник, который намного лучше описывает их, чем я.

Условное присвоение

x = 3 if (y == 1) else 2

Он делает именно то, на что это похоже: "присвойте 3 x, если y равен 1, иначе присвойте 2 x". Обратите внимание, что эти символы не нужны, но они мне нравятся для удобства чтения. Вы также можете связать это, если у вас есть что-то более сложное:

x = 3 if (y == 1) else 2 if (y == -1) else 1

Хотя в определенный момент это заходит слишком далеко.

Обратите внимание, что вы можете использовать if... else в любом выражении. Например:

(func1 if y == 1 else func2)(arg1, arg2) 

Здесь func1 будет вызываться, если y равно 1, а func2 в противном случае. В обоих случаях соответствующая функция будет вызываться с аргументами arg1 и arg2.

Аналогично, действует также следующее:

x = (class1 if y == 1 else class2)(arg1, arg2)

где class1 и class2 - это два класса.

Doctest: документация и юнит-тестирование одновременно.

Пример извлечен из документации Python:

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    If the result is small enough to fit in an int, return an int.
    Else return a long.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result

def _test():
    import doctest
    doctest.testmod()    

if __name__ == "__main__":
    _test()

Именованное форматирование

% -форматирование требует словаря (также применяется проверка%i/%s и т. д.).

>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
The answer is 42.

>>> foo, bar = 'question', 123

>>> print "The %(foo)s is %(bar)i." % locals()
The question is 123.

И поскольку locals() также является словарем, вы можете просто передать его как dict и иметь% -подстановки из ваших локальных переменных. Я думаю, что это осуждается, но упрощает вещи..

Новый стиль форматирования

>>> print("The {foo} is {bar}".format(foo='answer', bar=42))

Чтобы добавить больше модулей Python (особенно сторонних), большинство людей, похоже, используют переменные среды PYTHONPATH или добавляют символические ссылки или каталоги в свои каталоги пакетов сайтов. Другой способ, это использовать *.pth файлы. Вот официальное объяснение Python Doc:

"Самый удобный способ [изменить путь поиска Python] - добавить файл конфигурации пути в каталог, который уже находится на пути Python, обычно в каталог... / site-packages /. Файлы конфигурации пути имеют расширение.pth., и каждая строка должна содержать один путь, который будет добавлен к sys.path. (Поскольку новые пути добавляются к sys.path, модули в добавленных каталогах не будут переопределять стандартные модули. Это означает, что вы не можете использовать этот механизм для установки исправленных версий стандартных модулей.)"

Исключение остальное:

try:
  put_4000000000_volts_through_it(parrot)
except Voom:
  print "'E's pining!"
else:
  print "This parrot is no more!"
finally:
  end_sketch()

Использование предложения else лучше, чем добавление дополнительного кода в предложение try, поскольку оно позволяет избежать случайного перехвата исключения, которое не было вызвано кодом, защищаемым оператором try... Кроме.

См. http://docs.python.org/tut/node10.html

Возобновление исключения:

# Python 2 syntax
try:
    some_operation()
except SomeError, e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

# Python 3 syntax
try:
    some_operation()
except SomeError as e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

Оператор поднятия без аргументов в обработчике ошибок говорит Python повторно вызвать исключение с сохранением оригинальной трассировки, что позволяет вам сказать "о, извините, извините, я не хотел это улавливать, извините, извините."

Если вы хотите распечатать, сохранить или использовать оригинальную трассировку, вы можете получить ее с помощью sys.exc_info(), и распечатать ее, как это сделал бы Python, с помощью модуля traceback.

Основные сообщения:)

import this
# btw look at this module's source :)

Дешифрованный:

Дзен Питона, Тим Питерс

Красиво лучше, чем безобразно.
Явное лучше, чем неявное.
Простое лучше, чем сложное.
Сложный лучше, чем сложный.
Квартира лучше, чем вложенная.
Разреженный лучше, чем плотный.
Читаемость имеет значение.
Особые случаи не настолько особенные, чтобы нарушать правила.
Хотя практичность превосходит чистоту.
Ошибки никогда не должны проходить бесшумно.
Если явно не молчать.
Перед лицом двусмысленности откажитесь от соблазна гадать. Должен быть один - и желательно только один - очевидный способ сделать это.
Хотя этот путь может быть неочевидным на первый взгляд, если вы не голландец.
Сейчас лучше, чем никогда.
Хотя никогда не бывает лучше, чем сейчас.
Если реализацию сложно объяснить, это плохая идея.
Если реализацию легко объяснить, это может быть хорошей идеей.
Пространства имен - одна из отличных идей - давайте сделаем больше!

Завершение интерактивной вкладки переводчика

try:
    import readline
except ImportError:
    print "Unable to load readline module."
else:
    import rlcompleter
    readline.parse_and_bind("tab: complete")


>>> class myclass:
...    def function(self):
...       print "my function"
... 
>>> class_instance = myclass()
>>> class_instance.<TAB>
class_instance.__class__   class_instance.__module__
class_instance.__doc__     class_instance.function
>>> class_instance.f<TAB>unction()

Вам также нужно будет установить переменную окружения PYTHONSTARTUP.

Перегрузка оператора для set встроенная:

>>> a = set([1,2,3,4])
>>> b = set([3,4,5,6])
>>> a | b # Union
{1, 2, 3, 4, 5, 6}
>>> a & b # Intersection
{3, 4}
>>> a < b # Subset
False
>>> a - b # Difference
{1, 2}
>>> a ^ b # Symmetric Difference
{1, 2, 5, 6}

Более подробная информация из стандартной справочной библиотеки: Набор типов

Вложенные списки и выражения генератора:

[(i,j) for i in range(3) for j in range(i) ]    
((i,j) for i in range(4) for j in range(i) )

Они могут заменить огромные куски кода с вложенными циклами.

Другие вопросы по тегам