Непонятно, какой лучший способ создать класс со статическими методами, которые должны быть переопределяемыми. Я попытаюсь объяснить на примере.Статические методы и проектирование для наследования
У нас есть класс Goat
с методом can_climb
. (Кстати, это Python 3, в Python 2 Я хотел бы написать class Goat(object):
.)
class Goat:
def __init__(self, *args):
...
def can_climb(self, mountain):
return mountain.steepness < 10
billy = Goat("Billy")
if billy.can_climb(mount_everest):
print("wow Billy, impressive")
Это работает, как задумано, но метод can_climb
не использует self
. Кажется более чистым сделать его статическим методом, а pylint даже дает предупреждение для вышеуказанного метода. Так давайте изменим, что:
class Goat:
def __init__(self, *args):
...
@staticmethod
def can_climb(mountain):
return mountain.steepness < 10
billy = Goat("Billy")
if billy.can_climb(mount_everest):
print("wow Billy, impressive")
billy.can_climb
ближе к концу, а не может быть Goat.can_climb
и это не будет иметь значение в этом примере. Некоторые люди могут даже видеть это более ясным и более прямым, чтобы вызвать статический метод через свой класс, а не экземпляр.
Однако это приводит к тонкому ошибки, когда мы используем наследование и полиморфизм вводится:
class Goat:
def __init__(self, *args):
...
@staticmethod
def can_climb(mountain):
return mountain.steepness < 10
class MountaineeringGoat(Goat):
@staticmethod
def can_climb(mountain):
return True
billy = get_ourselves_a_goat_called_billy()
if Goat.can_climb(mount_everest): # bug
print("wow Billy, impressive")
Код, который вызывает Goat.can_climb
знает, что billy
является экземпляром Goat
и делает ошибочное предположение о том, что его тип Goat
, когда действительно может быть любой подтип Goat
. Или, возможно, это неправильно предполагает, что подклассы не будут переопределять статический метод.
Это кажется мне легкой ошибкой; возможно, Goat
не имел подклассов во время введения этой ошибки, поэтому ошибка не была замечена.
Как мы можем создавать и документировать класс, чтобы избежать такого рода ошибок? В частности, должен ли can_climb
из этого примера быть статическим или вместо этого использовать что-то другое?
Вы уже продемонстрировали, как избежать ошибки; если вам не нужен определенный класс, вызовите методы (и атрибуты) в экземпляре. И имеют модульные тесты, которые обнаруживают функциональные регрессии. – jonrsharpe
Я еще не убежден, что мой дизайн - лучший дизайн. Возможно, мне действительно нужно использовать метод экземпляра? – flornquake
@jonrsharpe Я ищу способы избежать этой проблемы со стороны класса, а не со стороны вызывающего, если это возможно. – flornquake