Скрытые особенности Python

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

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

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

Ответ 1

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

>>> 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, но с меньшим количеством ввода и каждый термин оценивается только один раз.

Ответ 2

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

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

К счастью, python может печатать дерево синтаксического анализа regex, передавая недокументированный экспериментальный скрытый флаг 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)

Ответ 4

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

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

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

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

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)

Ответ 5

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

Например:

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

Функция iter(callable, until_value) повторно вызывает callable и возвращает ее результат до тех пор, пока until_value не будет возвращен.

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

from __future__ import braces

Ответ 9

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

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

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

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

Ответ 10

Декораторы

Decorators позволяют обернуть функцию или метод в другую функцию, которая может добавить функциональность, изменить аргументы или результаты и т.д. Вы пишете декораторов на одну строку над определением функции, начиная с знака "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

Ответ 11

Синтаксис 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")

Ответ 12

Начиная с 2.5, у dicts есть специальный метод __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]}

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

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

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

Ответ 13

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

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

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

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

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

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

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

Ответ 14

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

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

Пример подробного синтаксиса (из Погружение в 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)

>>> 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})$"

Ответ 15

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

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

Например:

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)

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

Ответ 16

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

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

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

Ответ 17

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

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

что точно совпадает с

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

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

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

Изменить. Скорректировано название, чтобы более точно описать эту функцию.

Ответ 18

Контекстные менеджеры и оператор < <20 >

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

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

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

from __future__ import with_statement

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

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

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

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

Ответ 19

Словари имеют метод get()

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

Это отлично подходит для таких вещей, как добавление номеров:

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

Ответ 20

дескрипторы

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

Когда вы используете точечный доступ для поиска члена (например, x.y), 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, таковы, как они есть.

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

Ответ 21

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

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

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

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 - два класса.

Ответ 22

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()

Ответ 23

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

% -formatting принимает словарь (также применяется валидация% 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))

Ответ 24

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

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

Ответ 25

Исключение else:

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

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

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

Ответ 26

Исключения для повторного создания:

# 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)

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

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

Ответ 27

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

import this
# btw look at this module source :)

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

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

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

Ответ 28

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

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.

Ответ 29

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

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

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

Ответ 30

Перегрузка оператора для встроенного 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}

Подробнее из стандартной ссылки на библиотеку: Установить типы