Документация Python неофициальный перевод

doanddont.md

249 строк · 17.4 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/2.6/howto/doanddont.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Идиомы и анти-идиомы в Python89| Автор: | Moshe Zadka |10| --- | --- |1112Этот документ находится в общественном достоянии.1314Аннотация1516Этот документ можно рассматривать как дополнение к руководству. В нём показано, как использовать Python и, что ещё важнее, как *не* использовать Python.1718## Конструкции языка, которые не следует использовать1920Хотя в Python относительно мало «подводных камней» по сравнению с другими языками, в нём всё ещё есть конструкции, которые полезны только в крайних случаях или откровенно опасны.2122### from module import \*2324#### Внутри определений функций2526`from module import *` *недопустим* внутри определений функций. Хотя во многих версиях Python эта недопустимость не проверяется, это не делает конструкцию более допустимой – не более, чем наличие умного адвоката делает человека невиновным. Никогда не используйте её подобным образом. Даже в версиях, где она принималась, это замедляло выполнение функции, поскольку компилятор не мог быть уверен, какие имена являются локальными, а какие глобальными. В Python 2.1 эта конструкция вызывает предупреждения, а иногда и ошибки.2728#### На уровне модуля2930Хотя использовать `from module import *` на уровне модуля допустимо, обычно это плохая идея. Во-первых, это лишает Python важного свойства – возможности узнать, где определён каждый глобальный идентификатор, с помощью простой функции поиска в вашем любимом редакторе. Вы также создаёте себе проблемы в будущем, если какой-либо модуль обрастёт дополнительными функциями или классами.3132Один из самых ужасных вопросов, задаваемых в новостной группе: почему этот код:3334```python35f = open("www")36f.read()37```3839Не работает. Конечно, он прекрасно работает (если у вас есть файл с именем «www».) Но он не работает, если где-то в модуле присутствует оператор `from os import *`. Модуль [`os`](https://python-all.ru/2.6/library/os.html#module-os) содержит функцию [`open()`](https://python-all.ru/2.6/library/functions.html#open), которая возвращает целое число. Хотя это очень полезно, переопределение встроенной функции – одно из наименее полезных его свойств.4041Помните: вы никогда не можете знать наверняка, какие имена экспортирует модуль, поэтому либо берите то, что нужно – `from module import name1, name2`, либо оставляйте их в модуле и обращайтесь по мере необходимости – `import module;print module.name`.4243#### Когда это вполне допустимо4445Бывают ситуации, когда `from module import *` вполне уместно:4647- Интерактивная оболочка. Например, `from math import *` превращает Python в отличный научный калькулятор.48- При расширении модуля на C модулем на Python.49- Когда модуль заявляет, что он безопасен для `from import *`.5051### Неукрашенные [`exec`](https://python-all.ru/2.6/reference/simple_stmts.html#exec), [`execfile()`](https://python-all.ru/2.6/library/functions.html#execfile) и им подобные5253Слово «неукрашенные» означает использование без явного словаря, и в этом случае эти конструкции выполняют код в *текущем* окружении. Это опасно по тем же причинам, что и `from import *`: может перезаписать переменные, на которые вы рассчитываете, и нарушить работу остального кода. Просто не делайте так.5455Плохие примеры:5657```python58>>> for name in sys.argv[1:]:59>>>     exec "%s=1" % name60>>> def func(s, **kw):61>>>     for var, val in kw.items():62>>>         exec "s.%s=val" % var  # недопустимо!63>>> execfile("handler.py")64>>> handle()65```6667Хорошие примеры:6869```python70>>> d = {}71>>> for name in sys.argv[1:]:72>>>     d[name] = 173>>> def func(s, **kw):74>>>     for var, val in kw.items():75>>>         setattr(s, var, val)76>>> d={}77>>> execfile("handle.py", d, d)78>>> handle = d['handle']79>>> handle()80```8182### from module import name1, name28384Это «нельзя», которое гораздо слабее предыдущих «нельзя», но всё равно то, чего не следует делать, если нет веских причин. Обычно это плохая идея, потому что у вас внезапно появляется объект, живущий в двух разных пространствах имён. Когда привязка в одном пространстве имён меняется, привязка в другом не меняется, поэтому между ними возникает расхождение. Это происходит, например, при перезагрузке модуля или изменении определения функции во время выполнения.8586Плохой пример:8788```python89# foo.py90a = 19192# bar.py93from foo import a94if something():95    a = 2 # опасность: foo.a != a96```9798Хороший пример:99100```python101# foo.py102a = 1103104# bar.py105import foo106if something():107    foo.a = 2108```109110### except:111112В Python есть конструкция `except:`, которая перехватывает все исключения. Поскольку *любая* ошибка в Python вызывает исключение, это приводит к тому, что многие ошибки программирования выглядят как проблемы времени выполнения, и затрудняет процесс отладки.113114Следующий код показывает наглядный пример:115116```python117try:118    foo = opne("file") # опечатка в "open"119except:120    sys.exit("could not open file!")121```122123Вторая строка вызывает исключение [`NameError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.NameError), которое перехватывается блоком except. Программа завершится, и вы не будете знать, что это не имеет никакого отношения к читаемости `"file"`.124125Лучше написать так:126127```python128try:129    foo = opne("file") # будет изменён на "open", как только будет запущен130except IOError:131    sys.exit("could not open file")132```133134Есть несколько ситуаций, в которых конструкция `except:` полезна: например, в фреймворке при выполнении колбэков – желательно не позволять ни одному колбэку нарушать работу фреймворка.135136## Исключения137138Исключения – полезная возможность Python. Стоит научиться возбуждать их всякий раз, когда происходит что-то неожиданное, и перехватывать только там, где можно на них повлиять.139140Ниже приведён очень популярный антипаттерн.141142```python143def get_status(file):144    if not os.path.exists(file):145        print "file not found"146        sys.exit(1)147    return open(file).readline()148```149150Рассмотрим случай, когда файл удаляется между вызовом [`os.path.exists()`](https://python-all.ru/2.6/library/os.path.html#os.path.exists) и вызовом [`open()`](https://python-all.ru/2.6/library/functions.html#open). Это означает, что последняя строка вызовет исключение [`IOError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.IOError). То же самое произойдёт, если *file* существует, но нет прав на чтение. Поскольку при тестировании на обычной машине с существующими и несуществующими файлами ошибка не проявляется, результаты тестирования будут выглядеть нормально, и код будет выпущен. Тогда необработанное исключение [`IOError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.IOError) доходит до пользователя, который вынужден смотреть на некрасивую трассировку.151152Вот более правильный способ:153154```python155def get_status(file):156    try:157        return open(file).readline()158    except (IOError, OSError):159        print "file not found"160        sys.exit(1)161```162163В этой версии либо файл открывается и строка читается (так что это работает даже на ненадёжных соединениях NFS или SMB), либо выводится сообщение и приложение завершается.164165Тем не менее, `get_status()` делает слишком много предположений – что она будет использоваться только в коротком скрипте, а не, скажем, в долго работающем сервере. Конечно, вызывающий код мог бы сделать что-то вроде166167```python168try:169    status = get_status(log)170except SystemExit:171    status = None172```173174Поэтому старайтесь использовать как можно меньше конструкций `except` в коде – обычно они нужны как универсальный обработчик в `main()` или внутри вызовов, которые всегда должны завершаться успешно.175176Итак, вероятно, лучший вариант:177178```python179def get_status(file):180    return open(file).readline()181```182183Вызывающий код может обработать исключение, если захочет (например, если он перебирает несколько файлов в цикле), или просто позволить исключению всплыть к *своему* вызывающему.184185Последний вариант тоже не очень хорош – из-за особенностей реализации файл не будет закрыт при возникновении исключения, пока не завершится обработчик, а возможно, и вовсе не будет закрыт в реализациях, отличных от C (например, Jython).186187```python188def get_status(file):189    with open(file) as fp:190        return fp.readline()191```192193## Использование встроенных средств194195Время от времени люди зачем-то снова пишут то, что уже есть в библиотеке Python, и обычно плохо. Хотя некоторые модули имеют неудачный интерфейс, в целом гораздо лучше использовать богатую стандартную библиотеку и типы данных, входящие в Python, чем изобретать свои собственные.196197Полезный модуль, о котором мало кто знает – это [`os.path`](https://python-all.ru/2.6/library/os.path.html#module-os.path). Он всегда содержит правильные операции с путями для вашей операционной системы и обычно намного лучше того, что вы придумаете сами.198199Сравните:200201```python202# фу!203return dir+"/"+file204# лучше205return os.path.join(dir, file)206```207208Ещё полезные функции из [`os.path`](https://python-all.ru/2.6/library/os.path.html#module-os.path): `basename()`, `dirname()` и `splitext()`.209210Есть также много полезных встроенных функций, о которых люди почему-то не знают: [`min()`](https://python-all.ru/2.6/library/functions.html#min) и [`max()`](https://python-all.ru/2.6/library/functions.html#max) могут найти минимум/максимум любой последовательности со сравниваемой семантикой, например, но многие пишут свои собственные [`max()`](https://python-all.ru/2.6/library/functions.html#max)/[`min()`](https://python-all.ru/2.6/library/functions.html#min). Ещё одна очень полезная функция – [`reduce()`](https://python-all.ru/2.6/library/functions.html#reduce). Классическое использование [`reduce()`](https://python-all.ru/2.6/library/functions.html#reduce) выглядит примерно так211212```python213import sys, operator214nums = map(float, sys.argv[1:])215print reduce(operator.add, nums)/len(nums)216```217218Этот маленький скрипт выводит среднее арифметическое всех чисел, переданных в командной строке. [`reduce()`](https://python-all.ru/2.6/library/functions.html#reduce) суммирует все числа, а остальное – лишь небольшая пред- и постобработка.219220Кстати, обратите внимание, что [`float()`](https://python-all.ru/2.6/library/functions.html#float), [`int()`](https://python-all.ru/2.6/library/functions.html#int) и [`long()`](https://python-all.ru/2.6/library/functions.html#long) принимают строковые аргументы и потому подходят для разбора – при условии, что вы готовы обработать [`ValueError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.ValueError), которое они возбуждают.221222## Использование обратного слеша для продолжения инструкций223224Поскольку Python рассматривает перевод строки как завершитель инструкции, и поскольку инструкции часто длиннее, чем удобно помещать в одну строку, многие делают так:225226```python227if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \228   calculate_number(10, 20) != forbulate(500, 360):229      pass230```231232Следует понимать, что это опасно: случайный пробел после `\` сделает эту строку неверной, а случайные пробелы, как известно, трудно заметить в редакторах. В этом случае будет хотя бы синтаксическая ошибка, но если бы код был:233234```python235value = foo.bar()['first'][0]*baz.quux(1, 2)[5:9] \236        + calculate_number(10, 20)*forbulate(500, 360)237```238239то ошибка была бы неявной.240241Обычно гораздо лучше использовать неявное продолжение внутри скобок:242243Эта версия защищена от ошибок:244245```python246value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]247        + calculate_number(10, 20)*forbulate(500, 360))248```249