Рассмотрены примеры таких конструкций + некоторые очевидные, но не менее опасные конструкции, которых в коде желательно избегать. Статья рассчитана на python программистов с опытом 0 — 1,5 года. Опытные разработчики могут в коментах покритиковать или дополнить своими примерами.
1. Lambda.
переменная pw в lambda — ссылка на переменную, а не на значение. К моменту вызова функции переменная pw равна 5
Проблемный код
|
Решение: Передавать все переменные в lambda явно
|
2. Отличный порядок поиска атрибутов при ромбоидальном наследовании в классах классического и нового стиля
|
|
3. Изменяемые объекты в качестве значений по умолчанию
Магия:
|
Решение:
|
4. Значения по умолчанию инициализируются единожды
import random
def get_random_id(rand_id=random.randint(1, 100)):
return rand_id
print get_random_id() # 53
print get_random_id() # 53 ???
print get_random_id() # 53 ???
5. Не учтена иерархия исключений. Если вы не держите в голове подобные списки docs.python.org/2/library/exceptions.html#exception-hierarchy + списки исключений встроенных модулей + иерархию исключений вашего приложения, а также не используете PyLint. То можно написать следующее:
KeyError никогда не отработает
try:
d = {}
d['xxx']
except LookupError:
print '1'
except KeyError:
print '2'
6. Кэширование интерпретатором коротких строк и чисел
str1 = 'x' * 100
str2 = 'x' * 100
print str1 is str2 # False
str1 = 'x' * 10
str2 = 'x' * 10
print str1 is str2 # True ???
7. Неявная конкатенация.
Пропущенная запятая не генерирует исключений а приводит к слиянию смежных строк. Такая ситуация может произойти когда после последнего элемента в кортеже не поставили необязательную запятую, а затем строки в кортеже перегруппировали, отсортировали
tpl = (
'1_3_dfsf_sdfsf',
'3_11_sdfd_jfg',
'7_17_1dsd12asf sa321fs afsfffsdfs'
'11_19_dfgdfg211123sdg sdgsdgf dsfg',
'13_7_dsfgs dgfdsgdg',
'24_12_dasdsfgs dgfdsgdg',
)
8. Природа булевого типа.
True и False это самые настоящие 1 и 0, для которых придумали специальные название для большей выразительности языка.
try:
print True + 10 / False * 3
except Exception as e:
print e # integer division or modulo by zero
>>> type(True).__mro__
(<type 'bool'>, <type 'int'>, <type 'object'>)
docs.python.org/release/2.3.5/whatsnew/section-bool.html
Python's Booleans were added with the primary goal of making code clearer.
To sum up True and False in a sentence: they're alternative ways to spell the integer values 1 and 0, with the single difference that str() and repr() return the strings 'True' and 'False' instead of '1' and '0'.
7. Устаревшая конструкция. Опасна тем что потеря слэша, либо перенос только первой части выражения не приводит в очевидным ошибкам. Как решение — использовать круглые скобки.
x = 1 + 2 + 3
+ 4
9. Операторы сравнения and, or в отличии например от PHP не возвращают True или False, а возвращают один из элементов сравнения, в этом примере current_lang будет присвоен первый положительный элемент
current_lang = from_GET or from_Session or from_DB or DEFAULT_LANG
Такое выражение как альтернатива тернарному оператору так же будет работать, но стоит обратить внимание, что если первый элемент списка окажется '', 0, False, None, то будет возвращён последний элемент в сравнении, а нет первый элемент списка.
a = ['one', 'two', 'three']
print a and a[0] or None # one
10. Перехватить все исключения и при этом никак их не обработать. В этом примере ничего необычного не происходит, но такая конструкция таит в себе опасность и весьма популярна среди начинающих. Так писать не стоит, даже если вы уверены на 100% что исключение можно никак не обрабатывать, так как это не гарантирует что другой разработчик не допишет в блок try-except строку, исключение от которой хотелось бы всё таки зафиксировать. Решение: пишите в лог, конкретизируйте перехватываемый тип исключений, обрамляйте в try-except лишь минимально необходимый кусок кода.
try:
# Много кода, чем больше тем хуже
except Exception:
pass
11. Переопределение объектов из bulit-in. В данном случае переопределяется объекты list, id, type. Использовать классические id, type в функции и класс list в модуле привычным образом не получится. Как решение — установить PyLint в свою IDE и следить что он подсказывает.
def list(id=DEFAULT_ID, type=TYPES.ANY_TYPE):
"""
W0622 Redefining built-in "id" [pylint]
"""
return Item.objects.filter(id=id, item_type=type)
12. Работающий код, без сюрпризов… для вас… А вот другой разработчик, использующий mod2.py весьма удивится, заметив, что один из атрибутов модуля вдруг неожиданно изменился. Как решение стараться избегать таких действий, или хотяб вводить в mod2.py функцию для переопределения атрибута. Тогда изучая mod2.py можно будет хотя б понять, что один из атрибутом модуля может меняться.
# mod1.py
import mod2
mod2.any_attr = '123'
PS: Критика, замечания, дополнения приветствуются.
Автор: niko83