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


1419

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

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

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

Ответы:


740

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

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


121
Это очень полезно. Он должен быть стандартным для всех языков. К сожалению, это не так.
stalepretzel

8
Вы должны добавить несколько примеров, которые возвращают false aswell. например, >>> 10 <x <20 False
ShoeLace

19
Это относится и к другим операторам сравнения, поэтому люди иногда удивляются, почему код, подобный (5 в [5] - True), является ложным (но для начала явно непитонно явно тестировать подобные логические выражения).
Майлз

19
Хорошо, но не упускайте равные возможности, такие как «in» и «=». «A в B == C в D» означает «(A в B) и (B == C) и (C в D)», что может быть неожиданным.
Чарльз Мерриам

15
Азафе: Сравнения Лиспа, естественно, работают именно так. Это не особый случай, потому что нет другого (разумного) способа интерпретации (< 1 x 10). Вы даже можете применить их к отдельным аргументам, например (= 10): cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Body/…
Кен,

512

Получите дерево разбора регулярных выражений 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)

3
За исключением разбора HTML с использованием регулярных выражений, это медленно и мучительно. Даже встроенный модуль парсера html не использует регулярные выражения для выполнения работы. И если html-модуль вам не нравится, существует множество модулей синтаксического анализа XML / HTML, которые выполняют свою работу без необходимости изобретать велосипед.
BatchyX

Ссылка на документацию по синтаксису вывода была бы отличной.
Personman

1
Это должна быть официальная часть Python, а не экспериментальная ... RegEx всегда хитр, и возможность отслеживать происходящее действительно полезно.
Cahit

460

перечисление

Оберните итерируемое с 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
>>>

Ссылки:


56
Я удивлен, что это обычно не рассматривается в уроках, говорящих о списках Python
Draemon

45
И все это время я кодировал так: для i в диапазоне (len (a)): ... и затем использовал [i], чтобы получить текущий элемент.
Фернандо Мартин

4
@ Берри Цакала: Насколько мне известно, это не считается устаревшим.
JAB

23
Черт возьми, это круто. я в xrange (len (a)): всегда был моим наименее любимым языком Python.
Человек

15
перечисление может начинаться с произвольного индекса, необязательно 0. Пример: 'для i, элемент в перечислении ( list , start = 1): print i, item' начнет перечисление с 1, а не с 0.
dmitry_romanov

419

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

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

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)

Вы могли бы также использовать понимание вложенного списка для этого, да?
Шапр

54
Особо следует отметить экономию памяти. Значения вычисляются по требованию, поэтому у вас никогда не будет полного результата понимания списка в памяти. Это особенно желательно, если позже вы перебираете только часть понимания списка.
Saffsd

19
Это не особенно «скрытый» imo, но также стоит отметить тот факт, что вы не можете перематывать объект генератора, тогда как вы можете повторять список несколько раз.
подает

13
Функция «без перемотки» генераторов может вызвать некоторую путаницу. В частности, если вы распечатываете содержимое генератора для отладки, а затем используете его для обработки данных, это не работает. Данные создаются, используются print (), а затем недоступны для обычной обработки. Это не относится к спискам, так как они полностью хранятся в памяти.
johntellsall

4
Аналогичный (дублирующий?) Ответ: stackoverflow.com/questions/101268/hidden-features-of-python/… Заметьте, однако, что ответ, который я здесь привел, упоминает ДЕЙСТВИТЕЛЬНО ХОРОШУЮ презентацию о силе генераторов. Вы действительно должны проверить это.
Денилсон Са Майя

353

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

Например:

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

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


Как новичок в Python, не могли бы вы объяснить, почему lambdaздесь необходимо ключевое слово?
SiegeX

@SiegeX без лямбды, f.read (1) будет оцениваться (возвращая строку) перед передачей в функцию iter. Вместо этого лямбда-функция создает анонимную функцию и передает ее ей.
jmilloy

339

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

>>> 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]

39
Это определенно одна из самых неприятных скрытых возможностей. Я сталкивался с этим время от времени.
Торстен Марек

77
Я понял, что это намного легче понять, когда узнал, что аргументы по умолчанию находятся в кортеже, который является атрибутом функции, например foo.func_defaults. Который, будучи кортежем, неизменен.
Роберт Россни

2
@grayger: при выполнении оператора def его аргументы оцениваются интерпретатором. Это создает (или связывает) имя к объекту кода (набор функций). Однако аргументы по умолчанию создаются как объекты во время определения. Это верно для любого времени объекта по умолчанию, но имеет значение только (демонстрируя видимую семантику), когда объект изменчив. Нет способа повторно связать это имя аргумента по умолчанию в закрытии функции, хотя оно может быть переопределено для любого вызова или вся функция может быть переопределена).
Джим Деннис

3
@ Роберт, конечно, кортеж аргументов может быть неизменным, но объекты, на которые он указывает, не обязательно неизменны.
пул

16
Один быстрый взлом, чтобы сделать вашу инициализацию немного короче: x = x или []. Вы можете использовать это вместо 2-х строчной инструкции if.
Дэйв Манкофф

317

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

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

Согласовано. Давайте рассматривать это как неприятный пример скрытой возможности Python :)
Rafał Dowgird

89
На других языках я считаю, что это волшебное устройство называется «переменная».
Finnw

5
сопрограммы должны быть сопрограммами, а генератор должен быть самим собой, без смешивания. Мега-отличная ссылка, разговоры и примеры по этому поводу здесь: dabeaz.com/coroutines
u0b34a0f6ae

31
@finnw: пример реализует нечто похожее на переменную. Однако эту функцию можно использовать многими другими способами ... в отличие от переменной. Также должно быть очевидно, что подобная семантика может быть реализована с использованием объектов (в частности, класса, реализующего метод вызова Python ).
Джим Деннис

4
Это слишком тривиальный пример для людей, которые никогда не видели (и, вероятно, не поймут) сопрограмм. Пример, который реализует скользящее среднее без риска переполнения переменной суммы, является хорошим.
Прашант Кумар

313

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

from __future__ import braces

122
Это зло :)
Джейсон Бейкер

37
>>> из __future__ импортировать фигурные скобки Файл "<stdin>", строка 1 Ошибка синтаксиса: нет шансов: P
Бенджамин В. Смит

40
это богохульство!
Берк Д. Демир

335
Я думаю, что у нас здесь может быть синтаксическая ошибка, разве это не должно быть "из __past__ import скобок"?
Билл К

47
от __cruft__ скобок импорта
Филлип B Олдхэм

305

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

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]

31
На мой взгляд, гораздо понятнее функция reversed (). >>> список (полностью измененный (диапазон (4))) [3, 2, 1, 0]
Кристиан Оудард

3
тогда как лучше написать "this ia string" [:: - 1]? обратное, похоже, не помогает
Берри Цакала

24
Проблема с reversed () состоит в том, что он возвращает итератор, поэтому, если вы хотите сохранить тип обратной последовательности (кортеж, строка, список, юникод, пользовательские типы ...), вам потребуется дополнительный шаг для его преобразования обратно. ,
Рафал Доугирд

6
def reverse_string (string): вернуть строку [:: - 1]
pi.

4
@pi Я думаю, что если кто-то знает достаточно, чтобы определить reverse_string, как у вас, то можно оставить [:: - 1] в вашем коде и быть довольным его значением и ощущением, что он уместен.
физика Майкл

289

Декораторы

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

54
При определении декораторов я бы рекомендовал декорировать @decorator. Он создает декоратор, который сохраняет сигнатуру функции при выполнении самоанализа. Более подробная информация здесь: phyast.pitt.edu/~micheles/python/documentation.html
sirwart,

45
Как это скрытая особенность?
Vetle

50
Ну, его нет в большинстве простых уроков по Python, и я наткнулся на него долгое время после того, как начал использовать Python. Это то, что я бы назвал скрытой функцией, примерно такой же, как и другие популярные посты здесь.
DzinX

16
vetler, вопросы требуют «менее известных, но полезных функций языка программирования Python». Как вы оцениваете «менее известные, но полезные функции»? Я имею в виду, как любой из этих ответов скрытые функции?
Johnd

4
@vetler Большинство вещей здесь едва ли «спрятаны».
Хамфри Богарт

288

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

218
Я думаю, что синтаксис for / else неудобен. Такое ощущение, что предложение else должно выполняться, если тело цикла никогда не выполняется.
Codeape

14
ах. Никогда этого не видел! Но я должен сказать, что это немного неправильно. Кто может ожидать, что блок else будет выполняться только в том случае, если break никогда не выполняется? Я согласен с codeape: похоже, что еще введено для пустых foos.
Дарен Томас

52
кажется, что ключевое слово должно быть наконец, а не иначе
Jiaaro

21
За исключением, наконец, уже используется таким образом, что этот набор всегда выполняется.

7
Определенно не должно быть «иначе». Может быть, «тогда» или что-то еще, а затем «еще», когда цикл никогда не выполнялся.
Тор Валамо

258

Начиная с 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]}

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


10
Я предпочитаю использовать setdefault. m = {}; m.setdefault ('foo', 1)
грейджер

22
@grayger имел в виду это m={}; m.setdefault('foo', []).append(1).
Кристиан Чиупиту

1
Однако есть случаи, когда принятие defaultdict очень удобно. Например, функция может перебирать значение и работает для неопределенных ключей без дополнительного кода, поскольку по умолчанию это пустой список.
Мариан

3
defaultdict лучше в некоторых случаях, чем setdefault, так как он не создает объект по умолчанию, если ключ отсутствует. setdefault создает его независимо от того, отсутствует он или нет. Если ваш объект по умолчанию стоит дорого, это может привести к снижению производительности - я получил приличное ускорение от одной программы, просто изменив все вызовы setdefault.
Whatang

2
defaultdictтакже более мощный, чем setdefaultметод в других случаях. Например, для счетчика - dd = collections.defaultdict(int) ... dd[k] += 1против d.setdefault(k, 0) += 1.
Майк Грэм,

247

Обмен значениями на месте

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

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

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

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

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

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


1
Использует ли это больше реальной памяти, чем традиционный способ? Я бы предпочел сделать, так как вы создаете кортеж вместо одной переменной подкачки
Натан

75
Это не использует больше памяти. Он использует меньше ... Я просто написал это в обоих направлениях и декомпилировал байт-код ... Компилятор оптимизирует, как вы надеетесь. Результаты dis показали, что он настраивает vars, а затем ROT_TWOing. ROT_TWO означает «поменять местами два самых высоких стека» ... На самом деле довольно привлекательно.
королевский

5
Вы также случайно указали на еще одну приятную особенность Python, которая заключается в том, что вы можете неявно создавать кортеж элементов, просто разделяя их запятыми.
asmeurer

3
Dana the Sane: присваивание в Python является оператором, а не выражением, поэтому выражение будет недействительным, если = имеет более высокий приоритет (т. Е. Оно было интерпретировано как a, (b = b), a).
HBN

5
Это наименее скрытая функция, которую я читал здесь. Хорошо, но подробно описано в каждом уроке по Python.
Тьяго Чавес

235

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

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

7
Я не знаю, действительно ли я считаю, что это особенность Python, у большинства движков RE есть подробный вариант.
Джереми Бэнкс

18
Да, но поскольку вы не можете сделать это в grep или в большинстве редакторов, многие люди не знают, что это там. Тот факт, что другие языки имеют эквивалентную функцию, не делает ее не очень полезной и малоизвестной функцией Python
Марк Бейкер

7
В большом проекте с большим количеством оптимизированных регулярных выражений (читай: оптимизирован для машин, но не для людей) я укусил пулю и преобразовал все из них в подробный синтаксис. Теперь познакомить новых разработчиков с проектами стало намного проще. Отныне мы вводим подробные RE в каждый проект.
Берк Д. Демир

Я бы предпочел просто сказать: сотни = "(CM | CD | D? C {0,3})" # 900 (CM), 400 (CD) и т. Д. В языке уже есть способ дать названия вещей, способ добавления комментариев и способ объединения строк. Зачем использовать специальный синтаксис библиотеки здесь для вещей, которые язык уже делает отлично? Кажется, это идет прямо против Эпиграммы Перлиса 9.
Кен,

3
@Ken: регулярное выражение не всегда может быть непосредственно в источнике, его можно прочитать из настроек или файла конфигурации. Разрешение комментариев или просто дополнительный пробел (для удобства чтения) может быть большой помощью.

222

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

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

Например:

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)

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


27
* также известен как оператор сплат
Габриэль

3
Мне нравится эта функция, но Pylint не к сожалению.
Стивен Полгер

5
Совет Пилинта не закон. Другой способ apply (callable, arg_seq, arg_map) устарел с 2.3.
Ян Вернье

1
Совет Пилинта не может быть законом, но это чертовски хороший совет. Отладка кода, который чрезмерно балуется такими вещами, - это настоящий ад. Как отмечает оригинальный постер, это полезный ярлык .
Андрей

2
Я видел, как это используется в коде однажды, и удивился, что он сделал. К сожалению, трудно найти в Google "Python **"
Фрейзер Грэм,

205

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

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

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

10
Большой! Обратите внимание, что строки байтов воспринимаются буквально, а строки юникода декодируются: trycevag h"Uryyb fgnpxbiresybj!"
u0b34a0f6ae

12
к сожалению это удалено из py3k
mykhal

9
Это хорошо для обхода антивируса.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

96
Это не имеет ничего общего с кодировкой, это просто Python, написанный на валлийском языке. :-P
Оливье Вердиер

33
Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn!
Мануэль Феррерия

183

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

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

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

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

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

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

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


8
Это имеет большой потенциал для полезности, например, JIT ORMs
Mark Cidade

8
Я использую его для генерации классов HTML-формы на основе динамического ввода. Очень хорошо!
пи.

15
Примечание: все классы создаются во время выполнения. Таким образом, вы можете использовать оператор 'class' внутри условной или внутри функции (очень полезно для создания семейств классов или классов, которые действуют как замыкания). Улучшение, которое приносит «тип», - это возможность аккуратно определять динамически генерируемый набор атрибутов (или баз).
spookylukey

1
Вы также можете создавать анонимные типы с пустой строкой, например: type ('', (object,), {'x': 'blah'})
bluehavana

3
Может быть очень полезным для инъекций кода.
Авиу Турцион

179

Менеджеры контекста и оператор " 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!')

За кулисами происходит то, что оператор with вызывает special __enter__и __exit__методы для объекта file. Сведения об исключении также передаются__exit__ если какое-либо исключение было вызвано из тела оператора with, что позволяет обрабатывать исключения там.

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

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


3
Я бы не одобрил обзор кода, который импортировал что-либо из будущего . Функции скорее симпатичны, чем полезны, и обычно они просто запутывают новичков в Python.
платный ботаник

6
Да, такие «милые» функции, как вложенные области видимости и генераторы, лучше оставить тем, кто знает, что они делают. И любой, кто хочет быть совместимым с будущими версиями Python. Для вложенных областей и генераторов «будущие версии» Python означают 2.2 и 2.5 соответственно. Для оператора with «будущие версии» Python означает 2.6.
Крис Б.

10
Это само собой разумеется, но с Python v2.6 + вам больше не нужно импортировать из будущего . теперь это ключевое слово первого класса.
fitzgeraldsteele

25
В 2.7 вы можете иметь несколько withs:) with open('filea') as filea and open('fileb') as fileb: ...
Остин Ричардсон

5
@ Остин Я не мог заставить этот синтаксис работать на 2.7. Это, однако, сработало: with open('filea') as filea, open('fileb') as fileb: ...
Вим

168

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

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

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

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


39
Также проверьте метод setdefault.
Дарен Томас

27
Кроме того, обратите внимание на коллекцию collection.defaultdict.
JFS

8
Если вы используете Python 2.7 или новее или 3.1 или новее, проверьте класс Counter в модуле коллекций. docs.python.org/library/collections.html#collections.Counter
Элиас Замария

О, чувак, все это время я занимался get(key, None). Не знал, что Noneбыло предоставлено по умолчанию.
Джордан Рейтер

152

Дескрипторы

Они - магия целого ряда основных функций 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, являются такими, какие они есть.

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


Это дубликат декораторов, не так ли? ( stackoverflow.com/questions/101268/… )
gecco

2
нет, декораторы и дескрипторы - это совершенно разные вещи, хотя в примере кода я создаю дескриптор-декоратор. :)
Ник Джонсон

1
Другой способ сделать это с помощью лямбды:foo = property(lambda self: self.__foo)
Пит Петерсон

1
@PetePeterson Да, но propertyсама реализована с помощью дескрипторов, о чем и шла моя статья .
Ник Джонсон

142

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

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


29
Задание не является особенной частью. Вы можете так же легко сделать что-то вроде: вернуть 3, если (у == 1), иначе 2.
Брайан

25
Этот альтернативный способ - первый раз, когда я видел запутанный Python.
Крейг МакКуин

3
Кайлебрукс: В этом случае это не булевы операторы. Он будет оценивать только 2, если bool (3) == False.
RoadieRich

15
эта кодировка в обратном стиле смущает меня. что-то вроде x = ((y == 1) ? 3 : 2)имеет больше смысла для меня
mpen

13
Я чувствую как раз противоположность @Mark, троичные операторы в стиле C всегда смущали меня, правая сторона или середина, что оценивается при ложном условии? Я предпочитаю троичный синтаксис Python.
Джеффри Харрис

141

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

6
Докуты, конечно, крутые, но мне очень не нравятся все те помехи, которые приходится вводить, чтобы проверить, что что-то должно вызывать исключение
ТМ.

60
Докуты переоценены и загрязняют документацию. Как часто вы тестируете автономную функцию без какого-либо setUp ()?
платный ботаник

2
кто сказал, что у вас не может быть настройки в doctest? написать функцию, которая генерирует контекст и возвращает locals()затем в вашем doctest do locals().update(setUp())= D
Jiaaro

12
Если для автономной функции требуется setUp, высоки шансы, что ее следует отделить от какого-либо несвязанного материала или поместить в класс. Пространство имен doctest класса затем может быть повторно использовано в doctests метода класса, так что оно немного похоже на setUp, только DRY и доступно для чтения.
Энди Михайленко

4
«Как часто вы тестируете автономную функцию» - много. Я нахожу, что тестирование часто возникает естественным образом в процессе проектирования, когда я выбираю фасады.
Грегг Линд

138

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

% -форматирование требует словаря (также применяется проверка% 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))

60
Будет прекращено и в конечном итоге заменено методом string ().
Константин

3
Именованное форматирование очень полезно для переводчиков, так как они, как правило, просто видят строку формата без имен переменных для контекста
pixelbeat

2
Появляется для работы в Python 3.0.1 (необходимо добавить скобки вокруг вызова печати).
Паси Саволайнен

9
хэш , а? Я вижу, откуда вы пришли.
Shylent

11
Форматирование% s не будет прекращено. str.format () определенно более питоничен, но на самом деле в 10 раз медленнее для простой замены строки. Я считаю, что форматирование% s все еще является наилучшей практикой.
Кеннет Рейтц

132

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

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


1
Я никогда не устанавливал связь между этим .pth файлом в каталоге site-packages из setuptools и этой идеей. классно.
Дэйв Паола

122

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

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.


8
+1 это круто. Если блок try выполняется без ввода каких-либо исключительных блоков, то вводится блок else. И тогда, конечно, последний блок выполняется
инспектор

Я наконец понимаю, почему «еще» там! Спасибо.
Тайнарон

Было бы больше смысла использовать продолжить, но я думаю, что оно уже занято;)
Павел Прагак

Обратите внимание, что в более старых версиях Python2 вы не можете иметь оба предложения: и finally: для одной и той же попытки: block
Кевин Хорн

1
@ Paweł Prażak, как отметил Кевин Хорн, этот синтаксис был введен после первоначального выпуска Python, и добавление новых зарезервированных ключевых слов в существующий язык всегда проблематично. Вот почему существующее ключевое слово обычно используется повторно (см. «Auto» в недавнем стандарте C ++).
Константин

114

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

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


Извините, но это общеизвестная и общая черта почти всех языков.
Лукас С.

6
Обратите внимание на выделенный курсивом текст. Некоторые люди будут делать raise eвместо этого, что не сохраняет первоначальную трассировку.
habnabit

12
Может быть, более магический, exc_info = sys.exc_info(); raise exc_info[0], exc_info[1], exc_info[2]эквивалентно этому, но вы можете изменить эти значения (например, изменить тип исключения или сообщение)
ianb

3
@ Лукас С. Ну, я этого не знал, и я рад, что здесь написано.
Э-Удовлетворение

я могу показывать свою молодость здесь, но я всегда использовал синтаксис Python 3 в Python 2.7 без проблем
wim

106

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

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

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

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

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



1
Есть идеи, почему источник был зашифрован таким образом? Это было просто для удовольствия, или была какая-то другая причина?
MiniQuark

42
способ написания источника идет вразрез с дзен!
Хасен


2
Я обновил свой /usr/lib/python2.6/this.py, заменив старый код этим, print s.translate("".join(chr(64<i<91 and 65+(i-52)%26 or 96<i<123 and 97+(i-84)%26 or i) for i in range(256)))и теперь он выглядит намного лучше !! :-D
Фортран

2
@MiniQuark: краткий урок истории: wefearchange.org/2010/06/import-this-and-zen-of-python.html

105

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

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.


2
Это очень полезная функция. Настолько, что у меня есть простой скрипт для его включения (плюс пара других улучшений самоанализа): pixelbeat.org/scripts/inpy
pixelbeat

43
IPython дает вам это плюс тонны других
полезных

Это было бы более полезно в приглашении pdb, чем в обычном приглашении python (поскольку IPython в любом случае служит этой цели). Тем не менее, похоже, что это не работает в приглашении pdb, возможно потому, что pdb связывает свой собственный для tab (что менее полезно). Я попытался вызвать parse_and_bind () в приглашении pdb, но он все еще не работал. Альтернатива получения приглашения pdb с помощью IPython - это больше работы, поэтому я склонен не использовать его.
haridsv

2
@haridsv - easy_install ipdbтогда вы можете использоватьimport ipdb; ipdb.set_trace()
Даг Харрис

1
На OSX [и я представляю себе другие системы, которые используют libedit] вы должны сделатьreadline.parse_and_bind ("bind ^I rl_complete")
Foo Bah

91

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

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

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


«для j в диапазоне (я)» - это опечатка? Обычно вы хотите фиксированные диапазоны для i и j. Если вы получаете доступ к 2d массиву, вы упустите половину своих элементов.
Питер Гибсон

У меня нет доступа к массивам в этом примере. Единственная цель этого кода - показать, что выражения из внутренних диапазонов могут получить доступ к выражениям из внешних. Побочным продуктом является список пар (x, y), таких что 4> x> y> 0.
Rafał Dowgird

2
Сорта, как двойное интегрирование в исчислении, или двойное суммирование.
Yoo

22
Ключевой момент, который нужно помнить здесь (что потребовало у меня много времени, чтобы понять), заключается в том, что порядок forоператоров должен быть написан в том порядке, в котором вы ожидаете, что он будет записан в стандартном цикле for, снаружи внутрь.
Сыкора

2
Чтобы добавить комментарий Sykora: представьте, что вы начинаете со стека fors и ifs yield xвнутри. Чтобы преобразовать это в выражение генератора, переместите xсначала, удалите все двоеточия (и yield) и заключите все это в круглые скобки. Вместо этого для понимания списка замените внешние скобки квадратными скобками.
Кен Арнольд

91

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

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


В этом руководстве частично docs.python.org/tutorial/datastructures.html#sets
XTL
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.