О наборе утки :
Утиному типированию способствует то, что он обычно не проверяет тип аргументов в теле методов и функций, полагается на документацию, понятный код и тестирование для обеспечения правильного использования.
О проверке аргументов (EAFP: проще просить прощения, чем разрешения). Адаптированный пример отсюда :
... это считается более питоническим:
def my_method(self, key):
try:
value = self.a_dict[member]
except TypeError:
# do something else
Это означает, что любому, кто использует ваш код, не нужно использовать настоящий словарь или подкласс - он может использовать любой объект, который реализует интерфейс отображения.
К сожалению, на практике это не так просто. Что если член в приведенном выше примере может быть целым числом? Целые числа неизменны - поэтому вполне разумно использовать их в качестве ключей словаря. Однако они также используются для индексации объектов типа последовательности. Если member является целым числом, то во втором примере можно пропустить списки и строки, а также словари.
Об ассертивном программировании:
Утверждения - это систематический способ проверки того, что внутреннее состояние программы соответствует ожидаемому программистом с целью выявления ошибок. В частности, они хороши для ловли ложных предположений, которые были сделаны во время написания кода, или злоупотребления интерфейсом другим программистом. Кроме того, они могут выступать в качестве встроенной документации в некоторой степени, делая предположения программиста очевидными. («Явное лучше, чем неявное».)
Упомянутые концепции иногда противоречат друг другу, поэтому я полагаюсь на следующие факторы при выборе: вообще не проверять данные, проводить строгую проверку или использовать утверждения:
Сильная проверка. Под строгой проверкой я подразумеваю повышение пользовательского исключения (
ApiError
например). Если моя функция / метод является частью общедоступного API, лучше проверить аргумент, чтобы показать хорошее сообщение об ошибке неожиданного типа. Под проверкой типа я подразумеваю не только использованиеisinstance
, но также и то, что передаваемый объект поддерживает необходимый интерфейс (типизирование утки). Хотя я документирую API и указываю ожидаемый тип, а пользователь может захотеть использовать мою функцию неожиданным образом, я чувствую себя безопаснее, когда проверяю предположения. Я обычно использую,isinstance
и если позже я хочу поддержать других типов или уток, я изменяю логику проверки.Напористое программирование. Если мой код новый, я использую много утверждений. Каковы ваши советы по этому поводу? Вы позже удаляете утверждения из кода?
Если моя функция / метод не является частью API, но передает некоторые его аргументы другому коду, не написанному, не изученному или протестированному мной, я делаю много утверждений в соответствии с вызываемым интерфейсом. Моя логика в этом - лучше провалиться в моем коде, тогда где-то на 10 уровней глубже в трассировке стека с непонятной ошибкой, которая вынуждает много отлаживать, а потом все равно добавлять утверждение в мой код.
Комментарии и советы о том, когда использовать или не использовать проверку типа / значения, утверждает? Извините за не лучшую формулировку вопроса.
Например, рассмотрим следующую функцию, где Customer
декларативная модель SQLAlchemy:
def add_customer(self, customer):
"""Save new customer into the database.
@param customer: Customer instance, whose id is None
@return: merged into global session customer
"""
# no validation here at all
# let's hope SQLAlchemy session will break if `customer` is not a model instance
customer = self.session.add(customer)
self.session.commit()
return customer
Итак, есть несколько способов обработки валидации:
def add_customer(self, customer):
# this is an API method, so let's validate the input
if not isinstance(customer, Customer):
raise ApiError('Invalid type')
if customer.id is not None:
raise ApiError('id should be None')
customer = self.session.add(customer)
self.session.commit()
return customer
или
def add_customer(self, customer):
# this is an internal method, but i want to be sure
# that it's a customer model instance
assert isinstance(customer, Customer), 'Achtung!'
assert customer.id is None
customer = self.session.add(customer)
self.session.commit()
return customer
Когда и почему вы будете использовать каждый из них в контексте утки, проверки типов, проверки данных?