О наборе утки :
Утиному типированию способствует то, что он обычно не проверяет тип аргументов в теле методов и функций, полагается на документацию, понятный код и тестирование для обеспечения правильного использования.
О проверке аргументов (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
Когда и почему вы будете использовать каждый из них в контексте утки, проверки типов, проверки данных?
источник
Ответы:
Позвольте мне привести некоторые руководящие принципы.
Принцип № 1. Как указано в http://docs.python.org/2/reference/simple_stmts.html, издержки производительности для утверждений можно удалить с помощью параметра командной строки, оставаясь при этом для отладки. Если производительность является проблемой, сделайте это. Оставь утверждения. (Но не делайте ничего важного в утверждениях!)
Принцип № 2. Если вы что-то утверждаете и будете иметь фатальную ошибку, используйте assert. Совершенно бесполезно заниматься чем-то другим. Если кто-то позже захочет это изменить, он может изменить ваш код или избежать вызова этого метода.
Принцип № 3. Не запрещайте что-либо только потому, что вы думаете, что это глупо. Так что, если ваш метод пропускает строки? Если это работает, это работает.
Принцип № 4. Запрещать вещи, которые являются признаками вероятных ошибок. Например, рассмотрите возможность передачи словаря опций. Если этот словарь содержит вещи, которые не являются допустимыми параметрами, то это признак того, что кто-то не понял ваш API или имел опечатку. Это может привести к опечатке, а не к тому, чтобы кто-то сделал что-то разумное.
Основываясь на первых двух принципах, ваша вторая версия может быть выброшена. Какой из двух других вы предпочитаете - дело вкуса. Что вы считаете более вероятным? То, что кто-то передаст не-клиента,
add_customer
и что-то сломается (в этом случае предпочтительна версия 3), или что кто-то в какой-то момент захочет заменить вашего клиента прокси-объектом некоторого вида, который отвечает на все правильные методы (в этом случае версия 1 является предпочтительной).Лично я видел оба режима отказа. Я бы предпочел перейти с версии 1 из общего принципа, что я ленивый, и он меньше печатает. (Также такого рода ошибки обычно имеют тенденцию обнаруживаться рано или поздно довольно очевидным способом. И когда я хочу использовать прокси-объект, я действительно раздражаюсь на людях, которые связали мне руки.) Но есть программисты, которых я уважаю, кто пошел бы в другую сторону.
источник