Пустой шаблон в недогруженном Python?

Время от времени я сталкиваюсь с кодом как это:

foo = Foo()
...
if foo.bar is not None and foo.bar.baz == 42:
   shiny_happy(...)

Который кажется, ну, в общем, unpythonic мне.

В Objective C можно отправить сообщения в ноль и получить ноль как ответ. Я всегда думал, что это довольно удобно. Конечно, возможно реализовать Пустой шаблон в Python, однако судящий по Google результатов дал мне, кажется, что это очень широко не используется. Почему это?

Или еще лучше — это была бы плохая идея позволить None.whatever не возвратить Ни один вместо того, чтобы повысить исключение?

18
задан Tomas Brambora 6 January 2010 в 17:59
поделиться

11 ответов

PEP 336 - Make None Callable предложил схожее свойство:

None should be a callable object that when call with any arguments не имеет побочных эффектов и возвращает Нет.

Причина, по которой она была отклонена, заключалась в том, что она просто "считается особенностью, при вызове которой Никто не допускает ошибки"

.
19
ответ дан 30 November 2019 в 05:48
поделиться

Нельзя ли попробовать, кроме как? Питонический способ говорит Легче попросить прощения, чем разрешения .

Итак:

try:
    if foo.bar.baz == 42:
        shiny_happy(...)
except AttributeError:
    pass #or whatever

Или сделать это без возможности заставить замолчать больше исключений, чем хотелось бы:

try:
    baz = foo.bar.baz
except AttributeError:
    pass # handle error as desired
else:
    if baz == 42:
        shiny_happy(...)
13
ответ дан 30 November 2019 в 05:48
поделиться

Как говорили другие, PEP 336 описывает, почему такое поведение.

Добавив что-то вроде "Groovy's " safe navigation operator" (?]. ), возможно, сделает вещи более элегантными в некоторых случаях:

foo = Foo()

if foo.bar?.baz == 42:
    ...
4
ответ дан 30 November 2019 в 05:48
поделиться

Я не думаю, что это хорошая идея. Вот почему. Предположим, у вас есть

foo.getValue()

Предположим, что getValue() возвращает число, или None если не найдено. Теперь предположим, что foo не было случайностью или ошибкой. В результате, он вернет None, и продолжит, даже если есть ошибка.

Другими словами, вы больше не сможете различить, нет ли значения (возвращает None) или есть ли ошибка (foo было None, если не найдено). Вы изменяете договор о возврате рутины с фактом, который не контролируется самой рутиной, в конечном итоге переписывая ее семантику.

.
3
ответ дан 30 November 2019 в 05:48
поделиться

Вот почему я не думаю, что это хорошая идея:

foo = Foo() // now foo is None

// if foo is None, I want to raise an exception because that is an error condition.
// The normal case is that I expect a foo back whose bar property may or may not be filled in.
// If it's not filled in, I want to set it myself.

if not foo.bar // Evaluates to true because None.bar is None under your paradigm, I think
  foo.bar = 42 // Now what?

Как бы вы справились с этим делом?

2
ответ дан 30 November 2019 в 05:48
поделиться

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

getattr(foo.bar, 'baz', default_value)
2
ответ дан 30 November 2019 в 05:48
поделиться

Лично я считаю, что нужно сделать исключение. Скажем так, по какой-то причине вы пишете программы для ракет на Python. Представьте себе атомную бомбу и скажем, что был метод под названием explode(timeToExplode) и timeToExplode был передан как None. Я думаю, что в конце концов вы были бы несчастны, когда проиграли войну, потому что не нашли этого в тестировании

.
1
ответ дан 30 November 2019 в 05:48
поделиться

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

Исключения следует использовать для исключительных условий, и как таковые перемещать проверки на редкие ситуации за пределы основного блока кода. Их не следует использовать, когда неучтенное значение является обычным условием (подумайте о том, чтобы запросить левое дитя дерева, во многих случаях -- все листья -- оно будет нулевым). Опять же, есть третья ситуация с функциями, возвращающими наборы значений, в которой, чтобы облегчить код, можно просто вернуть действительное пустое множество.

Я считаю, что следуя вышеприведенному совету, код намного более читабелен. Когда ситуация редка, не стоит беспокоиться о нуле (будет вызвано исключение), поэтому менее используемый код переносится из основного блока.

Когда ноль - это действительное возвращаемое значение, его обработка находится в основном потоке управления, но это хорошо, так как это обычная ситуация и как таковая часть основного алгоритма (не следуйте за нулевым краем на графике).

В третьем случае, при запросе значений у функции, которая, возможно, не сможет вернуть ни одного значения, возврат пустого набора упрощает код: можно предположить, что возвращаемый контейнер существует и обрабатывать все найденные элементы без добавления дополнительных проверок в коде.

И опять же, это всего лишь мнение, но будучи моим, я склонен следовать ему :)

.
1
ответ дан 30 November 2019 в 05:48
поделиться
foo = Foo()
...
if foo.bar is not None and foo.bar.baz == 42:
   shiny_happy(...)

Вышеизложенное можно очистить, используя тот факт, что Ничто не разрешается Ложно:

if foo.bar and foo.bar.baz == 42:
    shiny_happy(...)
else:
    not_happy(...)
1
ответ дан 30 November 2019 в 05:48
поделиться

Извините, но этот код питонический. Думаю, большинство согласится с тем, что на Python "explicit is better than implicit". Python - это язык, который легко читается по сравнению с большинством, и люди не должны победить это, написав загадочный код. Сделайте смысл очень ясным.

foo = Foo()
...
if foo.bar is not None and foo.bar.baz == 42:
    shiny_happy(...)

В этом примере кода ясно, что foo.bar иногда является None на этом пути кода, и что мы запускаем shiny_happy() только если это не None, а .baz == 42. Очень понятно, что здесь происходит и почему. То же самое нельзя сказать о нулевом шаблоне, или попытке ... кроме кода в одном из ответов, размещенных здесь. Одно дело, если ваш язык, как Objective-C или javascript, применяет нулевой шаблон, но на языке, где он вообще не используется, это просто создаст путаницу и код, который трудно прочитать. При программировании на питоне делайте то же, что и питонисты.

14
ответ дан 30 November 2019 в 05:48
поделиться

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

Я думаю, что удобство этой особенности было бы в лучшем случае случайным, тогда как немые ошибки случаются все время .


Конечно, SQL-подобный NULL был бы плох для тестирования, который на самом деле банкирует на предложениях, которые являются либо правдивыми, либо ложными. Рассмотрим этот код из юниттеста.py:

class TestCase:
    ...
    def failUnless(self, expr, msg=None):
        """Fail the test unless the expression is true."""
        if not expr: raise self.failureException, msg

Теперь предположим, что у меня есть тест, который делает так:

conn = connect(addr)
self.failUnless(conn.isOpen())

Предположим, что connect ошибочно возвращает ноль. Если я использую "нулевой паттерн", или язык имеет его встроенный, и connect равен нулю, то connect.isOpen() тоже равен нулю, и not conn.isOpen() тоже равен нулю, поэтому утверждение passes, несмотря на то, что соединение явно не разорвано.

Я склонен считать NULL одной из наихудших особенностей SQL. А то, что null бесшумно передает для объекта любого типа на других языках, не намного лучше. (Тони Хоар назвал нулевые ссылки "моей миллиардной ошибкой".). Нам нужно меньше таких вещей, не больше.

11
ответ дан 30 November 2019 в 05:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: