Python 2.x получил ошибки и мины - PullRequest
61 голосов
/ 10 февраля 2009

Цель моего вопроса - укрепить мою базу знаний с помощью Python и получить более полное представление о ней, включая знание ее недостатков и неожиданностей. Если говорить конкретно, меня интересует только интерпретатор CPython.

Я ищу что-то похожее на то, чему научились мои мины PHP вопрос, где некоторые ответы были мне хорошо известны, но пара была ужасающей.

Обновление: Видимо, один, может быть, два человека расстроены тем, что я задал вопрос, на который уже частично ответили за пределами переполнения стека. В качестве какого-то компромисса вот URL http://www.ferg.org/projects/python_gotchas.html

Обратите внимание, что один или два ответа здесь уже являются оригинальными из того, что было написано на сайте, указанном выше.

Ответы [ 23 ]

83 голосов
/ 10 февраля 2009

Выражения в аргументах по умолчанию вычисляются при определении функции, не при ее вызове.

Пример: рассмотреть возможность использования по умолчанию аргумента для текущего времени:

>>>import time
>>> def report(when=time.time()):
...     print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19

Аргумент when не меняется. Он оценивается при определении функции. Он не изменится, пока приложение не будет перезапущено.

Стратегия: вы не будете спотыкаться об этом, если вы по умолчанию аргументируете None, а затем сделаете что-то полезное, когда увидите это:

>>> def report(when=None):
...     if when is None:
...         when = time.time()
...     print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23

Упражнение: , чтобы убедиться, что вы поняли: почему это происходит?

>>> def spam(eggs=[]):
...     eggs.append("spam")
...     return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']
60 голосов
/ 10 февраля 2009

Вы должны знать, как переменные класса обрабатываются в Python. Рассмотрим следующую иерархию классов:

class AAA(object):
    x = 1

class BBB(AAA):
    pass

class CCC(AAA):
    pass

Теперь проверьте вывод следующего кода:

>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
>>> print AAA.x, BBB.x, CCC.x
3 2 3

Удивлены? Вы не будете, если будете помнить, что переменные класса внутренне обрабатываются как словари объекта класса. Для операций чтения , если имя переменной не найдено в словаре текущего класса, его ищут в родительских классах. Итак, следующий код снова, но с пояснениями:

# AAA: {'x': 1}, BBB: {}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
# AAA: {'x': 1}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
# AAA: {'x': 3}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
3 2 3

То же самое касается обработки переменных класса в экземплярах классов (этот пример рассматривается как продолжение предыдущего):

>>> a = AAA()
# a: {}, AAA: {'x': 3}
>>> print a.x, AAA.x
3 3
>>> a.x = 4
# a: {'x': 4}, AAA: {'x': 3}
>>> print a.x, AAA.x
4 3
38 голосов
/ 10 февраля 2009

Циклы и лямбды (или любое замыкание, действительно): переменные связаны name

funcs = []
for x in range(5):
  funcs.append(lambda: x)

[f() for f in funcs]
# output:
# 4 4 4 4 4

Обходной путь - создание отдельной функции или передача аргументов по имени:

funcs = []
for x in range(5):
  funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4
20 голосов
/ 10 февраля 2009

Динамическое связывание делает нахождение в именах переменных на удивление трудным для поиска. Легко потратить полчаса на исправление тривиальной ошибки.

РЕДАКТИРОВАТЬ: пример ...

for item in some_list:
    ... # lots of code
... # more code
for tiem in some_other_list:
    process(item) # oops!
18 голосов
/ 26 ноября 2010

Один из самых больших сюрпризов, которые у меня когда-либо были с Python, это:

a = ([42],)
a[0] += [43, 44]

Это работает так, как и следовало ожидать, за исключением вызова ошибки TypeError после обновления первой записи кортежа! Таким образом, a будет ([42, 43, 44],) после выполнения оператора +=, но в любом случае будет исключение. Если вы попробуете это с другой стороны

a = ([42],)
b = a[0]
b += [43, 44]

вы не получите ошибку.

16 голосов
/ 10 декабря 2010
try:
    int("z")
except IndexError, ValueError:
    pass

причина, по которой это не работает, заключается в том, что IndexError - это тип исключения, которое вы перехватываете, а ValueError - имя переменной, которой вы назначаете исключение.

Правильный код для перехвата нескольких исключений:

try:
    int("z")
except (IndexError, ValueError):
    pass
12 голосов
/ 10 февраля 2009

Некоторое время назад было много дискуссий о скрытых языковых возможностях: hidden-features-of-python . Где были упомянуты некоторые подводные камни (и некоторые хорошие вещи тоже).

Также вы можете попробовать Python Warts .

Но для меня целочисленное деление - это гоча:

>>> 5/2
2

Вы, вероятно, хотели:

>>> 5*1.0/2
2.5

Если вы действительно хотите это (C-like) поведение, вы должны написать:

>>> 5//2
2

Как это будет работать и с плавающими (и это будет работать, когда вы в конечном итоге перейдете на Python 3 ):

>>> 5*1.0//2
2.0

GvR объясняет, как сработало целочисленное деление, как это происходит в истории Python .

11 голосов
/ 13 мая 2011

Нарезка списка доставила мне много горя. Я на самом деле считаю следующее поведение ошибкой.

Определить список x

>>> x = [10, 20, 30, 40, 50]

Индекс доступа 2:

>>> x[2]
30

Как и следовало ожидать.

Разрезать список от индекса 2 до конца списка:

>>> x[2:]
[30, 40, 50]

Как и следовало ожидать.

Индекс доступа 7:

>>> x[7]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Опять, как вы ожидаете.

Однако , попробуйте разделить список от индекса 7 до конца списка:

>>> x[7:]
[]

???

Средство защиты состоит в том, чтобы поставить много тестов при использовании нарезки списка. Я хотел бы просто получить ошибку вместо этого. Гораздо проще отлаживать.

10 голосов
/ 21 мая 2009

Не включая __init__ .py в ваших пакетах. Этот до сих пор иногда получает меня.

9 голосов
/ 11 февраля 2009

Джеймс Дюмей красноречиво напомнил мне еще об одной ошибке Python:

Не все «включенные батареи» Python замечательны .

Примером Джеймса были библиотеки HTTP: httplib, urllib, urllib2, urlparse, mimetools и ftplib. Некоторые функции дублируются, а некоторые ожидаемые функции полностью отсутствуют, например обработка перенаправления. Честно говоря, это ужасно.

Если мне когда-нибудь придется что-то захватывать через HTTP, я использую модуль urlgrabber , разветвленный из проекта Yum.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...