Должен ли я проверять типы аргументов конструктора (и в других местах тоже)?

StackOverflow https://stackoverflow.com/questions/602046

  •  03-07-2019
  •  | 
  •  

Вопрос

Python не рекомендует проверять типы.Но во многих случаях это может быть полезно:

  1. Проверка аргументов конструктора.например ,проверка логического значения противника, строки, dict и т.д.Если я этого не сделаю и установлю членам объекта значения аргументов, это вызовет проблемы позже.

  2. Проверка аргументов функций.

  3. В свойствах.Если кто-то установит неправильное значение или другой тип, я должен быстро отреагировать.

Это было полезно?

Решение

Простой ответ таков НЕТ, использовать Полиморфизм, Исключения и т.д.

  1. В случае, если аргументы конструктора имеют неправильный тип, при выполнении кода, зависящего от параметра определенного типа, будет выдано исключение.Если это странная вещь, специфичная для домена, создайте свое собственное исключение.Окружите блоки кода, которые, скорее всего, завершатся неудачей при исключении попытки, и обработайте ошибки.Поэтому лучше использовать обработку исключений.(То же самое относится и к аргументам функции)

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

В Python вы относитесь к программистам как к разумным существам!!Просто хорошо документируйте свой код (делайте вещи очевидными), создавайте Исключения там, где это уместно, пишите полиморфный код и т.д.Оставьте обработку исключений (только там, где это уместно) / ошибки в построении клиентскому коду.

Предупреждение
Предоставление обработки исключений клиентам не означает, что вы должны вываливать кучу ненужных ошибок на невольного пользователя.Если это вообще возможно, обрабатывайте исключения, которые могут возникнуть из-за неправильной конструкции или любой другой причины в самом вашем коде.Ваш код должен быть надежным.Если вы не можете справиться с ошибкой, вежливо сообщите об этом пользователю / программисту клиентского кода!

Примечание
В общем, плохие аргументы конструктора - это не то, о чем я слишком сильно беспокоюсь.

Другие советы

Ответ почти всегда будет "нет".Общая идея в Python, Ruby и некоторых других языках, которые мы назвали "Утиный набор текста".Вас не должно волновать, что это такое, только то, как это работает.Другими словами, "если все, что вам нужно, - это что-то крякающее, вам не нужно проверять, действительно ли это утка".

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

Проверка типов проверяет только на наличие одной из многих возможных ошибок в коде.Например, он не включает проверку диапазона (по крайней мере, не в Python).Современный ответ на утверждение о необходимости проверки типов заключается в том, что более эффективно разрабатывать модульные тесты, которые гарантируют правильность не только типов, но и функциональности.

Другая точка зрения заключается в том, что вы должны относиться к пользователям вашего API как к взрослым людям, согласившимся на это, и доверять им правильное использование API.Конечно, бывают случаи, когда проверка входных данных полезна, но это встречается реже, чем вы думаете.Одним из примеров являются данные из ненадежных источников, например, из общедоступного Интернета.

Проверяйте все, что вам нравится, вы просто должны быть откровенны.Следующий пример представляет собой конструктор из модуль в стандартной библиотеке - он проверяет 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)

Часто это хороший поступок.Проверка на наличие явных типов, вероятно, не так полезна в Python (как говорили другие), но проверка на наличие допустимых значений может быть хорошей идеей.Причина, по которой это хорошая идея, заключается в том, что программное обеспечение выйдет из строя ближе к источнику ошибки (это следует принципу быстрого сбоя).Кроме того, проверки служат документацией для других программистов и для вас самих.Еще лучше, это "исполняемая документация", что хорошо, потому что это документация, которая не может лгать.

Быстрый и грязный, но разумный способ проверить ваши аргументы - использовать assert:

def my_sqrt(x):
    assert x >= 0, "must be greater or equal to zero"
    # ...

Отстаивание своих аргументов - это своего рода Замысел бедняка по Контракту.(Возможно, вы захотите посмотреть "Дизайн по контракту";это интересно.)

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 в этом предложении, потому что я хочу установить члены экземпляра Только если тест прошел успешно, даже если код изменен или дополнен.Очевидно, вам не обязательно делать это таким образом.

Для аргументов функции и настройки свойств я бы не стал проводить эти тесты заранее, я бы просто использовал предоставленные объекты и действовал в соответствии с генерируемыми исключениями, если только подозрительные объекты не будут использоваться после длительного процесса.

"Если я этого не сделаю и установлю членам объекта значения аргументов, это вызовет проблемы позже".

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

  • Неужели это вообще не сработает?Вот для чего нужны блоки try / except.

  • Будет ли он вести себя "странно"?Это действительно редкое явление и ограничено типами и операторами, находящимися на грани промаха.Стандартным примером является деление.Если вы ожидали целых чисел, но получили значение с плавающей запятой, то деление может сделать не то, что вы хотели.Но это исправлено с помощью //, vs./ операторы деления.

  • Будет ли это просто неправильно, но все равно будет казаться завершенным?Это действительно редкость и потребовало бы довольно тщательно проработанного типа, который использовал бы стандартные имена, но делал нестандартные вещи.Например

    class MyMaliciousList( list ):
        def append( self, value ):
            super( MyMaliciousList, self ).remove( value )
    

Кроме этого, трудно допустить, чтобы что-то "вызывало проблемы позже".Пожалуйста, дополните свой вопрос конкретными примерами "проблем".

Как говорит дальке, ответ почти всегда "нет".В Python вас обычно не волнует, что параметр a является определенный тип, а скорее то, что он ведет себя как определенный типаж.Это известно как "Утиный набор текста".Существует два способа проверить, является ли параметр ведет себя как данный тип:(1) вы можете использовать его, как если бы он вел себя так, как вы ожидаете, и генерировать исключение, когда / если это не так, или (2) вы можете определить интерфейс, который описывает, как этот тип должен веди себя прилично и проверьте соответствие этому интерфейсу.

zope.интерфейс это моя предпочтительная интерфейсная система для Python, но есть несколько других.С помощью любого из них вы определяете интерфейс, затем объявляете, что данный тип соответствует этому интерфейсу, или определяете адаптер, который превращает ваш тип во что-то, что действительно соответствует этому интерфейсу.Затем вы можете утверждать (или тестировать по своему усмотрению), что параметры предоставляют (в терминологии zope.interface) этот интерфейс.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top