Должен ли я проверять типы аргументов конструктора (и в других местах тоже)?
Python не рекомендует проверять типы. Но во многих случаях это может быть полезно:
-
Проверка аргументов конструктора. например проверка foe Boolean, string, dict и т.д. Если я этого не сделаю и установите члены объекта в аргументы, это вызовет проблемы позже.
-
Проверка аргументов функций.
-
В свойствах. Если кто-то задает неправильное значение или другой тип, я должен быстро ответить.
Ответы
Ответ 1
Простой ответ Нет, используйте Полиморфизм, Исключения и т.д.
-
Если аргументы конструктора имеют неправильный тип, при выполнении кода, зависящем от параметра, относящегося к определенному типу, будет генерироваться исключение. Если это странная, специфичная для домена вещь, поднимите свое собственное исключение. Блоки окружения кода, которые могут сбой с ошибками try-except и handle. Поэтому лучше использовать обработку исключений. (То же самое касается аргументов функции)
-
В свойствах применяется тот же аргумент. Если вы проверяете полученное значение, используйте утверждение, чтобы проверить его диапазон и т.д. Если значение имеет неправильный тип, оно все равно будет терпеть неудачу. Затем обработайте AssertionError.
В Python вы рассматриваете программистов как интеллектуальных существ! Просто хорошо документируйте свой код (сделайте что-то очевидным), поднимите Исключения, где это необходимо, напишите полиморфный код и т.д. Оставьте обработку исключений (где это только подходит)/ошибки в построении для кода клиента.
Внимание
Уход с обработкой исключений для клиентов не означает, что вы должны вытаскивать много ошибок с мусором у невольного пользователя. Если это вообще возможно, обрабатывайте исключения, которые могут возникнуть из-за плохой конструкции или любой другой причины в вашем коде. Ваш код должен быть надежным. Если вы не можете справиться с этой ошибкой, вежливо сообщите программисту-программисту пользователя/клиента!
Примечание
В общем, плохие аргументы конструктору - это не то, о чем я беспокоюсь слишком много.
Ответ 2
Ответ почти всегда "нет". Общая идея в Python, Ruby и некоторых других языках называется " Duck Typing. Тебе все равно, что это такое, только как это работает. Другими словами," если все, что вам нужно, - это что-то такое, что вам не нужно проверять, что это на самом деле утка".
В реальной жизни проблема с установкой всех этих проверок типов заключается в невозможности заменить входы альтернативными реализациями. Вы можете проверить dict, но я могу передать что-то, в котором не является dict, но реализует dict API.
Проверка типов проверяет только одну из многих возможных ошибок в коде. Например, он не включает проверку диапазона (по крайней мере, не на Python). Современный ответ на утверждение о необходимости проверки типа заключается в том, что более эффективно разрабатывать модульные тесты, которые гарантируют правильность типов не только, но и правильность функциональности.
Другая точка зрения заключается в том, что вы должны относиться к своим пользователям API, как к соглашающимся взрослым, и доверять им правильно использовать API. Конечно, есть моменты, когда проверка ввода полезна, но она менее распространена, чем вы думаете. Один пример вводится из ненадежных источников, например из общедоступной сети.
Ответ 3
Проверьте все, что вам нравится, просто нужно быть явным. Следующий пример - это конструктор модуль в стандартной библиотеке - он проверяет extrasaction
arg:
class DictWriter:
def __init__(self, f, fieldnames, restval="", extrasaction="raise",
dialect="excel", *args, **kwds):
self.fieldnames = fieldnames # list of keys for the dict
self.restval = restval # for writing short dicts
if extrasaction.lower() not in ("raise", "ignore"):
raise ValueError, \
("extrasaction (%s) must be 'raise' or 'ignore'" %
extrasaction)
self.extrasaction = extrasaction
self.writer = writer(f, dialect, *args, **kwds)
Ответ 4
Часто бывает хорошо. Проверка на явные типы, вероятно, не так полезна в Python (как говорили другие), но проверка правовых значений может быть хорошей идеей. Причина, по которой это хорошая идея, заключается в том, что программное обеспечение не сможет приблизиться к источнику ошибки (это следует за принципом Fail Fast). Кроме того, проверки действуют как документация для других программистов и вас самих. Еще лучше, это "исполняемая документация", что хорошо, потому что это документация, которая не может лежать.
Быстрый и грязный, но разумный способ проверить ваши аргументы - использовать assert:
def my_sqrt(x):
assert x >= 0, "must be greater or equal to zero"
# ...
Утверждение ваших аргументов - это своего рода плохой человек по контракту. (Возможно, вы захотите посмотреть Design by Contract, это интересно.)
Ответ 5
AFAIU, вы хотите удостовериться, что некоторые объекты ведут себя ( "следовать за интерфейсом" ) в более раннее время, чем в случае фактического использования. В вашем примере вы хотите знать, что объекты подходят во время создания экземпляра, а не когда они действительно будут использоваться.
Помня о том, что мы говорим здесь о Python, я не буду предлагать assert
(что если python -O
или переменная среды PYTHONOPTIMIZE установлена в 1, когда ваша программа работает?) или проверка определенных типов (потому что что излишне ограничивает типы, которые вы можете использовать), но я предлагаю ранние функции тестирования, что-то вроде строк:
def __init__(self, a_number, a_boolean, a_duck, a_sequence):
self.a_number= a_number + 0
self.a_boolean= not not a_boolean
try:
a_duck.quack
except AttributeError:
raise TypeError, "can't use it if it doesn't quack"
else:
self.a_duck= a_duck
try:
iter(a_sequence)
except TypeError:
raise TypeError, "expected an iterable sequence"
else:
self.a_sequence= a_sequence
Я использовал try… except… else
в этом предложении, потому что я хочу установить члены экземпляра только в том случае, если тест удался, даже если код был изменен или расширен. Вам не обязательно это делать, очевидно.
Для аргументов функции и свойств настройки я бы не стал делать эти тесты заранее, я бы просто использовал предоставленные объекты и действовал на выброшенные исключения, если только подозрительные объекты не будут использоваться после длительного процесса.
Ответ 6
"Если я этого не сделаю и установите члены объекта в аргументы, это вызовет проблемы позже".
Пожалуйста, будьте предельно ясны в точном списке "проблем", которые будут вызваны позже.
-
Не будет ли это вообще работать? Для чего нужны try/except blocks.
-
Будет ли он вести себя "странно"? Это действительно редко, и оно ограничено типами "ближнего пролета" и операторами. Стандартным примером является разделение. Если вы ожидали целых чисел, но получили с плавающей точкой, то разделение может не делать то, что вы хотели. Но это исправлено с операторами //, vs./division.
-
Будет ли это просто неправильно, но все же кажется, что оно завершено? Это очень редко, и для этого требуется довольно тщательно продуманный тип, который использовал стандартные имена, но делал нестандартные вещи. Например
class MyMaliciousList( list ):
def append( self, value ):
super( MyMaliciousList, self ).remove( value )
Кроме того, трудно заставить вещи "вызывать проблемы позже". Пожалуйста, обновите свой вопрос с помощью конкретных примеров "проблем".
Ответ 7
Как говорит Далке, ответ почти всегда "нет". В Python вам, как правило, не важно, что параметр является определенным типом, а скорее ведет себя как определенный тип. Это называется " Duck Typing". Существует два способа проверить, ведет ли параметр к определенному типу: (1) вы можете использовать его так, как если бы он вел себя так, как вы ожидаете, и генерирует исключение, если оно/нет, или (2) вы можете определить интерфейс, который описывает, как этот тип должен вести себя и проверить соответствие с этим интерфейсом.
zope.interface - моя предпочтительная система интерфейса для Python, но есть и другие. С любым из них вы определяете интерфейс, затем объявляете, что данный тип соответствует этому интерфейсу или определяет адаптер, который превращает ваш тип во что-то, что соответствует этому интерфейсу. Затем вы можете утверждать (или проверять, как пожелаете), что параметры предоставляют (в терминологии zope.interface) этот интерфейс.