Приховані особливості 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
Ви повинні додати кілька прикладів, які також повертають помилкові відгуки. наприклад >>> 10 <x <20 False
ShoeLace

19
Це стосується і інших операторів порівняння, тому люди іноді дивуються, чому код типу (5 в [5] є Істинним) є помилковим (але для початку явно перевіряти такі булеви, як нетипово, нетипово).
Майлз

19
Добре, але слідкуйте за рівними значеннями, наприклад "в" і "=". "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

Посилання на документацію на вихідний синтаксис було б чудово.
Особа

1
Це має бути офіційною частиною Python, а не експериментальною ... RegEx завжди складний і вміти відстежувати те, що відбувається, справді корисно.
Cahit

460

перерахувати

Оберніть ітерабельний номер із перерахуванням, і він отримає елемент разом із його індексом.

Наприклад:


>>> 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
Я здивований, що в навчальних посібниках, що розповідають про списки пітонів, це не висвітлюється звичайно.
Draemon

45
І весь цей час я кодував таким чином: для i in range (len (a)): ..., а потім за допомогою [i] отримати поточний елемент.
Фернандо Мартін

4
@Berry Tsakala: Наскільки мені відомо, це ще не застаріло.
JAB

23
Святе лайно це приголомшливо. для i in xrange (len (a)): завжди був моїм найулюбленішим ідіомою пітона.
Особа

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

419

Створення об’єктів генераторів

Якщо ти пишеш

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

ви можете дістати генератор і призначити його x. Тепер це означає, що ви можете зробити

for n in x:

Перевагою цього є те, що вам не потрібно проміжне зберігання, яке вам знадобиться, якби ви це зробили

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

У деяких випадках це може призвести до значного прискорення.

Ви можете додати багато, якщо заяви до кінця генератора, в основному реплікуючи вкладені для циклів:

>>> 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
Особливо слід відзначити економію накладних витрат на пам'ять. Значення обчислюються на вимогу, тому ви ніколи не маєте в пам'яті всього результату розуміння списку. Це особливо бажано, якщо пізніше ви переглянете лише частину розуміння списку.
сафсд

19
Це не особливо "приховане" іммо, але також варто відзначити той факт, що ви не могли перемотати об'єкт-генератор, тоді як ви можете повторно повторювати список будь-яку кількість разів.
подає

13
Особливість «без перемотування» генераторів може викликати певну плутанину. Зокрема, якщо ви друкуєте вміст генератора для налагодження, а потім використовувати його пізніше для обробки даних, воно не працює. Дані виробляються, споживаються print (), тоді вони недоступні для звичайної обробки. Це не стосується розуміння списку, оскільки вони повністю зберігаються в пам'яті.
johntellsall

4
Аналогічна відповідь (dup?): Stackoverflow.com/questions/101268/hidden-features-of-python/… Однак зауважте, що відповідь, до якої я посилаюся тут, згадує НАДАЛЬНО ДОБРУ презентацію про потужність генераторів. Ви дійсно повинні це перевірити.
Denilson Sá Maia

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) буде оцінено (повернення рядка), перш ніж перейти до функції ітера. Натомість лямбда створює анонімну функцію і передає її ітератору.
jmilloy

339

Будьте обережні з змінними аргументами за замовчуванням

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

39
Це, безумовно, одна з найбільш неприємних прихованих особливостей. Я стикався з цим час від часу.
Торстен Марек

77
Мені це було набагато простіше зрозуміти, коли я дізнався, що аргументи за замовчуванням містяться в кордоні, який є атрибутом функції, наприклад foo.func_defaults. Що, будучи кортежем, незмінне.
Роберт Россні

2
@grayger: Коли виконується оператор def, його аргументи оцінюються інтерпретатором. Це створює (або відновлює) ім'я до кодового об'єкта (набір функції). Однак аргументи за замовчуванням інстанціюються як об'єкти на момент визначення. Це стосується будь-якого об'єкта, дефолтується дефолт, але лише суттєвого (викриття видимої семантики), коли об'єкт є змінним. Немає можливості повторно прив’язувати це ім'я аргументу за замовчуванням при закритті функції, хоча, очевидно, його можна перезапустити для будь-якого виклику або всю функцію можна перезначити).
Джим Денніс

3
@ Роберт, звичайно, аргумент кортежу може бути незмінним, але об'єкти, на які він вказує, не обов'язково незмінні.
poolie

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

Домовились. Давайте
розглянемо

89
В інших мовах я вважаю, що цей магічний пристрій називають "змінною".
finnw

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

31
@finnw: приклад реалізує щось подібне до змінної. Однак функцію можна використовувати багатьма іншими способами ... на відміну від змінної. Слід також бути очевидним, що аналогічна семантика може бути реалізована за допомогою об'єктів (клас, що реалізує метод виклику Python , зокрема).
Джим Денніс

4
Це занадто банальний приклад для людей, які ніколи не бачили (і, мабуть, не зрозуміли) спільних процедур. Приклад, який реалізує поточну середню без ризику переповнення змінної суми, є хорошим.
Прашант Кумар

313

Якщо вам не подобається використовувати пробіл для позначення областей, ви можете використовувати стиль C {}, видавши:

from __future__ import braces

122
Це зло. :)
Джейсон Бейкер

37
>>> з __future__ імпорт брекетів Файл "<stdin>", рядок 1 SyntaxError: не шанс: P
Бенджамін У. Сміт

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

335
Я думаю, що у нас може виникнути синтаксична помилка, чи не повинно це бути "від __past__ імпортних брекетів"?
Білл К

47
з брекетів імпорту __cruft__
Філіп Б Олдхем,

305

Аргумент кроків в операторах фрагментів Наприклад:

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

Окремий випадок x[::-1]є корисною фразою для "x reversed".

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

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

3
то як краще написати "цей рядок ia" [:: - 1]? перевернуте, здається, не допоможе
Беррі Цакала

24
Проблема з reversed () полягає в тому, що він повертає ітератор, тому якщо ви хочете зберегти тип зворотної послідовності (кортеж, рядок, список, унікод, типи користувачів ...), вам потрібен додатковий крок, щоб перетворити його назад .
Rafał Dowgird

6
def reverse_string (рядок): return string [:: - 1]
pi.

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

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
Як це прихована особливість?
Ветле

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

16
Ветлер, питання задає "маловідомі, але корисні функції мови програмування Python". Як ви вимірюєте "маловідомі, але корисні функції"? Я маю на увазі, як приховуються будь-які з цих відповідей?
Джонд

4
@vetler Більшість речей тут навряд чи "приховані".
Хамфрі Богарт

288

Синтаксис для ... else (див. Http://docs.python.org/ref/for.html )

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

Блок "else", як правило, виконується в кінці циклу for, якщо тільки не викликається перерва.

Вищевказаний код можна імітувати так:

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

218
Я думаю, що синтаксис for / else незручний. Це "відчуває" так, ніби інше застереження має бути виконане, якщо тіло циклу ніколи не виконується.
codeape

14
ах. Ніколи цього не бачив! Але мушу сказати, що це трохи помилка. Хто очікує, що інший блок буде виконаний, лише якщо перерви ніколи не відбудуться? Я погоджуюсь з кодеєм: Схоже, що ще введено для порожніх фонів.
Дарен Томас

52
Здається, ключове слово має бути нарешті, а не інше
Jiaaro

21
За винятком того, що він використовується вже у такий спосіб, коли цей набір завжди виконується.

7
Насамперед не повинно бути "іншим". Можливо "тоді" чи щось, а потім "інше", коли цикл ніколи не виконувався.
Tor Valamo

258

Починаючи з 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]}

Існує також диктує підклас 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, щоб перевірити, чи існує елемент, який додав би новий елемент до диктату.


10
Я вважаю за краще використовувати setdefault. m = {}; m.setdefault ('foo', 1)
сірий

22
@grayger мав на увазі це m={}; m.setdefault('foo', []).append(1).
Крістіан Цюпіту

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

3
за замовчуванням в деяких випадках краще, ніж встановити за замовчуванням, оскільки він не створює об'єкт за замовчуванням, якщо ключ відсутній. 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
Він не використовує більше пам'яті. Він використовує менше .. Я просто написав це обома способами, і де-склав байт-код .. компілятор оптимізує, як би ви сподівалися. Результати дис показали, що це налаштування vars, а потім ROT_TWOing. ROT_TWO означає «поміняти дві найпопулярніші варі стеки» ... Насправді досить тьмяно.
королівський

5
Ви також ненавмисно вказуєте на ще одну приємну особливість Python, яка полягає в тому, що ви можете неявно зробити кортеж елементів, просто розділивши їх комами.
асмеурер

3
Dana Sane: присвоєння в Python - це вислів, а не вираз, так що вираз був би недійсним, якщо = мав більший пріоритет (тобто його інтерпретували як a, (b = b), a).
hbn

5
Це найменш прихована особливість, яку я тут читав. Приємно, але чітко описано в кожному навчальному посібнику Python.
Тіаго Чавес

235

Зрозумілі регулярні вирази

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

Приклад узгодження імен (з Regular Expression 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})$"

7
Я не знаю, чи дійсно я вважаю, що функція Python у більшості двигунів RE має багатослівний варіант.
Джеремі Бенкс

18
Так, але оскільки ви не можете зробити це в греп або в більшості редакторів, багато людей не знають, що це там. Той факт, що інші мови мають еквівалентну особливість, не робить його не корисною і маловідомою особливістю python
Mark Baker

7
У великому проекті з великою кількістю оптимізованих регулярних виразів (читайте: оптимізовано для машинного, але не для людського), я кусав кулю і перетворив їх у багатослівний синтаксис. Зараз представляти нових розробників у проекти набагато простіше. Відтепер ми застосовуємо багатослівні ПЕ на кожному проекті.
Берк Д. Демір

Я вважаю за краще сказати: сотні = "(CM | CD | D? C {0,3})" # 900 (CM), 400 (CD) тощо. У мові вже є спосіб надати речі імена, a спосіб додавання коментарів та спосіб поєднання рядків. Чому тут використовувати спеціальний синтаксис бібліотеки для речей, які мова вже ідеально добре? Здається, йде прямо проти "Епіграми Перліса" 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
Мені подобається ця особливість, але пілінт не сумує.
Стівен Полгер

5
поради пілінта - це не закон. Інший спосіб застосувати (callable, arg_seq, arg_map), застарілий з 2.3.
Янна Верньє

1
Поради пілінта можуть бути не законом, але це чортово гарні поради. Код налагодження, який надмірно поблажує подібні речі - це чистий пекло. Як зазначається в оригіналі плаката, це корисний ярлик .
Андрій

2
Я бачив, як це використовується в коді одного разу і цікавився, що він зробив. На жаль, важко гугл для "Python **"
Фрейзер Грем

205

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

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

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

10
Чудово! Зауважте, як рядки байтів приймаються буквально, але рядки Unicode розшифровуються: спробуйтеcevag 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 ORM
Mark Cidade

8
Я використовую його для створення класів HTML-форм на основі динамічного введення. Дуже хороший!
пі.

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

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

3
Може бути дуже корисним для введення коду.
Avihu Turzion

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!')

Що відбувається тут за лаштунками, це те, що оператор «з» викликає особливості __enter__та __exit__методи на файловому об’єкті. Деталі про винятки також передаються, __exit__якщо будь-який виняток був порушений з органом оператора з оператором, що дозволяє обробці виключень там відбуватися.

У цьому конкретному випадку це для вас - це те, що він гарантує закриття файлу, коли виконання виходить за межі 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
@Austin я не міг змусити цей синтаксис працювати на 2.7. це все-таки спрацювало: with open('filea') as filea, open('fileb') as fileb: ...
Вім

168

Словники мають метод get ()

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

Це чудово підходить для таких речей, як додавання чисел:

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


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

27
також перевірити collection.default клас.
jfs

8
Якщо ви використовуєте Python 2.7 або пізнішої версії, або 3.1 або пізнішої версії, перегляньте клас Counter у модулі колекції. docs.python.org/library/collections.html#collections.Counter
Elias Zamaria

О людино, весь цей час я робив 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 = 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, якщо (y == 1) else 2.
Брайан

25
Цей альтернативний шлях я вперше побачив заплутаного Python.
Крейг МакКуїн

3
Kylebrooks: У цьому випадку це не коротке замикання булевих операторів. Він оцінить лише 2, якщо bool (3) == False.
RoadieRich

15
це кодування в зворотному стилі мене бентежить. щось на зразок x = ((y == 1) ? 3 : 2)має більше сенсу для мене
mpen

13
Я відчуваю навпаки @Mark, потрійні оператори у стилі С завжди мене бентежать, чи правою стороною чи серединою є те, що оцінюється за хибною умовою? Я дуже віддаю перевагу потрійному синтаксису 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
Документи, безумовно, круті, але мені дуже не подобається все те, що вам потрібно набрати, щоб перевірити, що щось повинно стати винятком
TM.

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

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

12
Якщо автономна функція вимагає встановлення, велика ймовірність, що її слід відокремити від неспоріднених матеріалів або поставити в клас. Потім простір імен клас 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 () також є словником, ви можете просто передати це як диктант і мати% -заміни з ваших локальних змінних. Я думаю, це нахмуриться, але спрощує речі.

Форматування нового стилю

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

60
Буде припинено та врешті замінено методом format () рядка
Константин

3
Форматування
іменів

2
Здається, що працює в python 3.0.1 (потрібно для додавання батьківських тез навколо виклику друку).
Pasi Savolainen

9
хеш , а? Я бачу, звідки ти прийшов.
шилент

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

132

Щоб додати більше модулів python (особливо сторонніх), більшість людей, схоже, використовують змінні середовища PYTHONPATH або вони додають символьні посилання або каталоги у своїх каталогах пакунків сайтів. Інший спосіб - використовувати * .pth файли. Ось офіційне пояснення доктора python:

"Найзручніший спосіб [змінити шлях пошуку python] - це додавання файлу конфігурації шляху до каталогу, який вже знаходиться на шляху Python, як правило, до ... / site-пакети / каталог. Файли конфігурації контуру мають розширення .pth , і кожен рядок повинен містити єдиний шлях, який буде доданий до sys.path. (Оскільки нові шляхи додаються до sys.path, модулі в доданих каталогах не замінять стандартні модулі. Це означає, що ви не можете використовувати цей механізм для встановлення фіксованих версій стандартних модулів.) "


1
Я ніколи не робив зв'язку між цим .pth файлом у каталозі сайтів-пакунків із 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 ... крім оператора.

Див. Http://docs.python.org/tut/node10.html


8
+1 це дивовижно. Якщо блок спробу виконується, не вводячи жодних блоків виключень, тоді вводиться блок else. І тоді, звичайно, виконується остаточний блок
inspectorG4dget

Я нарешті зрозумію, чому "інше" є! Дякую.
taynaron

Було б більше сенсу використовувати продовжувати, але я думаю, це вже прийнято;)
Paweł Prażak

Зауважте, що у старих версіях Python2 ви не можете мати і те й інше: і нарешті: пункти для однієї і тієї ж спроби: block
Кевін Хорн,

1
@ Paweł Prażak, як згадував Кевін Хорн, цей синтаксис був введений після первинного випуску Python, і додавання нових зарезервованих ключових слів до існуючої мови завжди проблематично. Ось чому звичайне ключове слово зазвичай повторно використовується (див. "Авто" в останньому стандарті 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замість цього, що не зберігає оригінальний слід.
ханабіт

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

3
@Lucas S. Ну, я цього не знав, і радий, що це написано тут.
e-satis

я можу показувати свою молодість тут, але я завжди використовував синтаксис python 3 у python 2.7, без жодних питань
wim

106

Основні повідомлення :)

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

Дефіфіковано :

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

Красиве краще, ніж потворне.
Явне краще, ніж неявне.
Просте - краще, ніж складне.
Комплекс краще, ніж складний.
Квартира краще, ніж вкладена.
Розріджений краще, ніж щільний.
Читання рахується.
Особливі випадки недостатньо спеціальні для порушення правил.
Хоча практичність перемагає чистоту.
Помилки ніколи не повинні проходити мовчки.
Якщо явно мовчати.
В умовах неоднозначності відмовтеся від спокуси здогадатися. Повинно бути один - і бажано лише один - очевидний спосіб це зробити.
Хоча спочатку цей спосіб може бути не очевидним, якщо ви не голландці.
Зараз краще, ніж ніколи.
Хоча ніколи не буває краще, ніжпрямо зараз.
Якщо реалізацію важко пояснити, це погана ідея.
Якщо реалізацію легко пояснити, це може бути хорошою ідеєю.
Простори імен - це чудова ідея - давайте зробимо більше таких!


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

42
так, як написано джерело, йде проти дзену!
hasen


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 дає вам плюс багато інших акуратних речей
akaihola

Це було б кориснішим у запиті pdb, ніж у звичайному запиті python (оскільки IPython так чи інакше відповідає цій цілі). Однак, схоже, це не працює у запиті pdb, можливо, тому, що pdb прив'язує власну для вкладки (що менш корисно). Я спробував зателефонувати parse_and_bind () у запиті pdb, але все одно не вийшло. Альтернатива отримання підказки pdb з IPython - це більше роботи, тому я, як правило, не використовую її.
haridsv

2
@haridsv - easy_install ipdb- тоді ви можете використовуватиimport ipdb; ipdb.set_trace()
Doug Harris

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)" - це помилка друку? Зазвичай ви хочете фіксованих діапазонів для i та j. Якщо ви отримуєте доступ до 2d масиву, ви пропустите половину елементів.
Пітер Гібсон

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

2
подібні подвійні інтеграції в обчислення або подвійне підсумовування.
Yoo

22
Ключовим моментом, який слід пам’ятати тут (для цього мені знадобилося багато часу), є те, що порядок forвисловлювань повинен бути записаний у тому порядку, який ви очікуєте, щоб вони були написані у стандартному циклі, ззовні всередину.
sykora

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.