Python. Неочевидное поведение некоторых конструкций

от автора

Рассмотрены примеры таких конструкций + некоторые очевидные, но не менее опасные конструкции, которых в коде желательно избегать. Статья рассчитана на python программистов с опытом 0 — 1,5 года. Опытные разработчики могут в коментах покритиковать или дополнить своими примерами.

1. Lambda.
переменная pw в lambda — ссылка на переменную, а не на значение. К моменту вызова функции переменная pw равна 5

Проблемный код

to_pow = {} for pw in xrange(5):      to_pow[pw] = lambda x: x ** pw  print to_pow[2](10)  # 10000 ???  

Решение: Передавать все переменные в lambda явно

to_pow = {}                        for pw in xrange(5):     to_pow[pw] = lambda x, pw=pw: x ** pw  print to_pow[2](10)  # 100  

2. Отличный порядок поиска атрибутов при ромбоидальном наследовании в классах классического и нового стиля

class A():     def field(self):         return 'a'    class B(A):      pass    class C(A):      def field(self):         return 'c'    class Entity(B, C):     pass   print Entity().field()  # a !!! 

class A():     def field(self):         return 'a'    class B(A):      pass    class C(A, object):  # New style class      def field(self):         return 'c'    class Entity(B, C):      pass   print Entity().field()  # c !!! 

3. Изменяемые объекты в качестве значений по умолчанию

Магия:

def get_data(val=[]):      val.append(1)     return val  print get_data()  # [1] print get_data()  # [1, 1]    ??? print get_data()  # [1, 1, 1]    ??? 

Решение:

def get_data(val=None):      val = val or []     val.append(1)     return val   print get_data()  # [1]   print get_data()  # [1] 

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 

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: Критика, замечания, дополнения приветствуются.

ссылка на оригинал статьи http://habrahabr.ru/post/192098/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *