Полагаю, это не столько вопрос о характере типизации утки, сколько о том, как остаться питоническим.
Прежде всего - когда речь идет о диктовках, в частности, когда структура диктовок достаточно предсказуема, а данный ключ обычно не присутствует, но иногда это так, я сначала думаю о двух подходах:
if myKey in dict:
do_some_work(dict[myKey])
else:
pass
И, конечно, Е. Олде подход «прощение против разрешения».
try:
do_some_work(dict[myKey])
except KeyError:
pass
Будучи подмастерьем Python, я чувствую, что вижу, что последний предпочел много, что кажется странным, потому что в Python документы try/excepts
кажутся предпочтительными, когда есть реальная ошибка, а не ... отсутствие успеха?
Если случайный dict не имеет ключа в myDict, и известно, что он не всегда будет иметь этот ключ, попытка / исключает контекстуально вводит в заблуждение? Это не ошибка программирования, это просто факт данных - у этого слова просто не было этого конкретного ключа.
Это кажется особенно важным, когда вы смотрите на синтаксис try / exception / else, который выглядит очень полезным, когда нужно убедиться, что попытка не перехватывает слишком много ошибок. Вы можете сделать что-то вроде:
try:
foo += bar
except TypeError:
pass
else:
return some_more_work(foo)
Не приведет ли это к проглатыванию всевозможных странных ошибок, которые, вероятно, являются результатом какого-то плохого кода? Приведенный выше код может просто помешать вам увидеть, что вы пытаетесь добавить, 2 + {}
и вы, возможно, никогда не поймете, что какая-то часть вашего кода пошла ужасно неправильно. Я не предлагаю проверять все типы, поэтому это Python, а не JavaScript - но опять же с контекстом try / Кроме того, похоже, что он должен поймать программу, делающую то, что он не должен делать, вместо этого дать ему возможность продолжить.
Я понимаю, что приведенный выше пример является спорным аргументом и на самом деле намеренно плох. Но, учитывая питоническое убеждение, better to ask forgiveness than permission
я не могу не чувствовать, что возникает вопрос о том, где грань в песке на самом деле находится между правильным применением if / else против try / исключения, в частности, когда вы знаете, чего ожидать от данные, с которыми вы работаете.
Я даже не говорю о проблемах со скоростью или о лучших практиках, я просто немного сбит с толку воспринимаемой диаграммой Венна случаев, когда кажется, что это может пойти в любом случае, но люди ошибаются на стороне попытки / кроме потому что «кто-то где-то сказал, что это Pythonic». Я сделал неправильные выводы о применении этого синтаксиса?
Ответы:
Вместо этого используйте метод get :
... где
default_value
возвращаемое значение, еслиthe_key
не вsome_dict
. Если вы пропуститеdefault_value
,None
возвращается, если ключ отсутствует.В целом, в Python люди предпочитают попробовать / кроме проверки чего-либо сначала - см. Запись EAFP в глоссарии . Обратите внимание, что многие функции «проверки на членство» используют исключения за кулисами.
источник
dict
и выполняем работу, основываясь на том, что найдено, то кажется, чтоif myDict.get(a_key) is not None: do_work(myDict[a_key])
это более многословно и еще один пример неявного просмотра перед прыжком - плюс это немного менее читабельное ИМХО. Возможно, я не понимаюdict.get(a_key, a_default)
в правильном контексте, но кажется, что это предшествует написанию «not-a-switch-Statement if / elses» или магических значений. Мне нравится то, что делает этот метод, но я рассмотрю его глубже.data = myDict.get(a_key, default)
и либоdo_work
будете делать правильные вещи, когда даноdefault
(например.None
), Или вы делаетеif data: do_work(data)
. В любом случае нужен только один поиск. (Это может бытьif data is not None: ...
, в любом случае, это все еще только один поиск, и тогда ваша собственная логика может взять верх.)try/except/else
мусора и только в целом улучшило ИМХО читаемость моего кода. Я усвоил ценный урок, который слышал раньше, но сумел забыть принять близко к сердцу: «Если вы чувствуете, что пишете слишком много кода, остановитесь и посмотрите на возможности языка». Благодарность!!Отсутствующий ключ - это правило или исключение ?
С прагматической точки зрения бросать / ловить намного медленнее, чем проверять, находится ли ключ в таблице. Если пропущенный ключ является обычным явлением - вы должны использовать условие.
Еще одна вещь в пользу условия - это предложение else - гораздо легче понять, когда любой блок, если он выполнен, учитывает, что даже один и тот же класс исключения может быть сгенерирован из нескольких операторов в блоке try.
Код в условии кроме не должен быть частью нормального недостатка (например, если ключа нет, добавьте его) - он должен обрабатывать ошибку.
источник
try
/catch
версию быстрее.В духе того, чтобы быть "питоническим" и, в частности, печатать на утке - попытка / исключение выглядит почти хрупкой для меня.
Все, что вам действительно нужно, это что-то с доступом, похожим на диктовку, но
__getitem__
его можно изменить, чтобы сделать что-то не совсем так, как вы ожидаете. Например, используяdefaultdict
из стандартной библиотеки:Поскольку возвращаемое значение по умолчанию - Falsy, проверка доступа, подобная этой, будет работать в большинстве случаев просто отлично. Похоже, что код также будет проще, поэтому мои коллеги и я делали это месяцами.
К сожалению, несколько месяцев спустя, эти дополнительные ключи фактически стали причиной проблем и трудных для отслеживания ошибок, потому что
0
стали действительным значением. И иногда мы использовали бы,in
чтобы проверить, существует ли ключ, заставляя код делать разные вещи, когда он должен был быть идентичным.Причина, по которой я предполагаю, что она выглядит неработающей, заключается в том, что в духе Python по типу утиной печати все, что вам нужно, - это что-то похожее на диктат, и оно
defaultdict
идеально соответствует этому требованию, как и любой объект, от которого вы можете создать, который наследуетdict
. Вы не можете гарантировать, что вызывающая сторона не изменит свою реализацию в дальнейшем, поэтому вы должны делать это с наименьшими побочными эффектами.Используйте первую версию
if myKey in mydict
.Также обратите внимание, что речь идет именно о примере в вопросе. Кредо Python «Лучше просить прощения, чем разрешения» в значительной степени предназначено для обеспечения того, чтобы вы действительно действовали правильно, когда вы не получаете того, что хотите. Например, проверка, существует ли файл, и затем попытка чтения из него - по крайней мере, 3 вещи, которые я могу придумать, могут пойти не так:
Первый, на который вы можете попытаться «попросить разрешения», но по характеру условий гонки это не всегда будет работать; второй слишком легко забыть проверить; и третий не может быть проверен, не пытаясь прочитать файл. В любом случае, что вы делаете после провала почти наверняка будет то же самое, так что в этом примере, что это лучше «просить прощения», используя попробовать / за исключением.
источник