Нарушает ли подкласс int, запрещающий отрицательные целые числа, принцип подстановки Лискова?

Нарушает ли подкласс int, запрещающий отрицательные целые числа, принцип подстановки Лискова?
Нарушает ли подкласс int, запрещающий отрицательные целые числа, принцип подстановки Лискова? - ikukevk @ Unsplash

Это не нарушение LSP, потому что объект неизменяем и не перегружает какие-либо методы экземпляра несовместимым образом.

Принцип подстановки Лисков выполняется, если какие-либо свойства экземпляров супертипа также выполняются объектами подтипа.

Это поведенческое определение подтипа. Он не требует, чтобы подтип вел себя идентично супертипу, просто чтобы сохранялись все свойства, обещанные супертипом. Например, переопределенные методы в подтипе могут иметь более сильные постусловия и более слабые предусловия, но должны поддерживать инварианты супертипа. Подтипы также могут добавлять новые методы.

PositiveInteger экземпляры обладают теми же свойствами и возможностями, что и обычные int экземпляры. Действительно, ваша реализация ничего не меняет в поведении целочисленных объектов, а только меняет возможность создания такого объекта. Конструктор __new__ — это метод объекта класса, а не метод экземпляров класса. В стандартной библиотеке для этого есть прецедент: bool — аналогичный подтип int.

Однако некоторые предостережения:

  • Если бы объект int был изменчивым, это действительно было бы нарушением LSP: вы ожидали бы, что сможете установить int в отрицательное число, но ваш PositiveInteger должен был бы предотвратить это — нарушение одного из свойств экземпляров базового класса.

  • Это обсуждение LSP относится к экземплярам класса. Мы также можем рассмотреть, совместимы ли сами классы, которые также являются объектами. Здесь поведение изменилось, так что вы не можете заменить свой класс PositiveInteger. Например, рассмотрим следующий код:

    def make_num(value, numtype=int):
      return numtype(value)
    

    С аннотациями типов у нас может быть что-то вроде:

    Numtype = TypeVar('Numtype', int)
    def make_num(value: int, numtype: Type[Numtype]) -> Numtype:
      return numtype(value)
    

    В этом фрагменте использование вашего PositiveInteger в качестве Numtype, возможно, изменит поведение несовместимым образом, отклонив отрицательные целые числа.

    Другой способ несовместимости вашего конструктора с конструктором класса int заключается в том, что int(value) может принимать значения, отличные от int. В частности, передача строки будет анализировать строку, а передача числа с плавающей запятой приведет к ее усечению. Если вы пытаетесь обеспечить наиболее совместимый конструктор, вы можете исправить эту деталь, запустив value = int(value) перед проверкой положительности значения.


LetsCodeIt, 17 января 2023 г., 20:36