На Топтале
(toptal.com) есть подборка статей типа «10
наиболее распространенных ошибок у
программеров на Х», где Х — некий язык
программирования.
Нас, конечно,
в первую очередь интересует Python
Далее я кратенько
пройдусь по списку.
1. дефолтные
значения аргументов
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified ... bar.append("baz") # but this line could be problematic, as we'll see... ... return bar
попробуйте вызвать функцию подряд
несколько раз без параметра.
Фишка в том,
что дефолтная переменная инициируется
только один раз а не при каждом вызове.
Ну не знаю кто
как, а нас учили, что писать во входные
параметры — не православно. Посему на
эти грабли я никогда не наступал.
2. переменные
класса
>>> class A(object): ... x = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.x, B.x, C.x 1 1 1 >>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1 >>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3
What the $%#!&?? We only changed A.x. Why did C.x change too?
По моему тут
всё очевидно, класс С наследует переменную
от А. Опять же, нас учили использовать
конструкторы при работе с классами,
поэтому эти грабли тоже мне не знакомы.
3. параметры
при отлове иксепшенов
>>> try: ... l = ["a", "b"] ... int(l[2]) ... except ValueError, IndexError: # To catch both exceptions, right? ... pass ... Traceback (most recent call last): File "<stdin>", line 3, in <module> IndexError: list index out of range
чиста проблема с синтаксисом,
см.документацию.
Правильный
способ указать несколько отлавливаемых
иксепшенов
>>> try: ... l = ["a", "b"] ... int(l[2]) ... except (ValueError, IndexError) as e: ... pass
Должен признаться, грешен. Я, ленивая
обезьяна, обычно отлавливаю одно наиболее
общее исключение, хотя это и не православно.
Зато работает. Грабли мимо.
4. запись в
глобальные переменные
>>> x = 10 >>> def foo(): ... x += 1 ... print x ... >>> foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment
читать можно, писать уже не получается.
Во первых, писать в глобальные переменные
— mauvais ton, простите мой французский. Во
вторых, учим матчасть, ключевое слово
global. Обратно мимо.
5. перебирая
список менять его
>>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): ... if odd(numbers[i]): ... del numbers[i] # BAD: Deleting item from a list while iterating over it ... Traceback (most recent call last): File "<stdin>", line 2, in <module> IndexError: list index out of range
Без комментариев. Что-то я начал
раздражаться, что это за грабли они
выкапывают?
Ну это вообще
ни в какие ворота. Где они находят таких
программистов? Руки за это отрывать
сразу. Детский сад какой-то. Если это
одна из наиболее частых ошибок
программистов на Топтале, то не желаю
иметь с ними никаких программистских
дел.
6. позднее
связывание
>>> def create_multipliers(): ... return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): ... print multiplier(2) ... You might expect the following output: 0 2 4 6 8 But you actually get: 8 8 8 8 8
На момент вызова лямбдоидов, переменная
i уже посчитана и заново не высчитывается.
Как-то мне не
попадались такие случаи. Можеть быть
потому, что я лямбды не слишком уважаю?
Опять же, зачем генерировать список,
когда можно генерировать итератор?
Вообще,
подтверждаю потенциальные грабли,
увлекшись можно и наступить.
7. циклические
зависимости в модулях
# In a.py: import b def f(): return b.x print f() # And in b.py: import a x = 1 def g(): print a.f()
Теоретически, такое случается. Если уж
это произошло, следует разобраться с
архитектурой, дизайном и прочей
декомпозицией. Что-то тут не так. Надо
переделать. А если переделывать лень,
делайте импорт внутри функции, это
спасет нерадивую обезьяну.
Не грабли это
а грязный дизайн.
8. использование
имен зарезервированных стандартными
библиотеками.
Опять же, мойте
руки перед едой и чистите зубы после
простые правила гигиены позволяют
избегать такой напасти. Неймспейсы,
префиксы и постфиксы, не поддавайтесь
соблазну использовать общеупотребимые
имена типа «count», «len» и проч. Если кто
на эти грабли и наступает, то только
новичок.
9. разница
между Python 2 и Python 3
например,
области видимости
import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()
В Python 2 это работает как и ожидается. В
третьем мы получим
UnboundLocalError: local variable 'e' referenced before assignment
Честно говоря, называть ошибками
программиста то, что в Python 3 не работает
код написанный для Python 2, это наглость.
Но в данном конкретном случае мы наблюдаем
опять нарушение гигиены. Для доступа к
содержимому иксепшенов снаружи их блока
необходимо сохранить значение во внешней
переменной. И не называть ее тем же
именем. Все таки, опыт написания программ
на C/C++ сильно помогает не ходить по таким
«граблям», поверьте. А лучше проверьте
на себе.
10. порядок
уничтожения объектов
import foo class Bar(object): ... def __del__(self): foo.cleanup(self.myhandle)
при завершении интерпретатора он сначала
зачистит глобальные объекты, поэтому
foo.cleanup страшно обломается.
Теоретически,
я могу представить себе ситуацию, когда
в деструкторе надо вызвать внешний
модуль. С другой стороны, нафига нужен
деструктор, срабатывающий при закрытии
интерпретатора? Все ресурсы будут
освобождены по любому. В целом, настолько
редкая ситуация, что мне ни разу не
попадалась. Но, теоретически, может.
Потенциальные грабли для меня №2.
Общий вывод:
тесты, тесты и еще раз тесты. Не думайте,
что вы знаете как работает ваша программа.
Только тесты могут доказать, что работает
она как задумано.
Ну и, конечно,
надо себя похвалить — я крут. Из 10
популярных граблей мне грозят только
2 штуки и то очень потенциально.
original post http://vasnake.blogspot.com/2014/07/10.html
Комментариев нет:
Отправить комментарий