2016-06-06 5 views
2

Непонятно, какой лучший способ создать класс со статическими методами, которые должны быть переопределяемыми. Я попытаюсь объяснить на примере.Статические методы и проектирование для наследования


У нас есть класс 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 из этого примера быть статическим или вместо этого использовать что-то другое?

+3

Вы уже продемонстрировали, как избежать ошибки; если вам не нужен определенный класс, вызовите методы (и атрибуты) в экземпляре. И имеют модульные тесты, которые обнаруживают функциональные регрессии. – jonrsharpe

+0

Я еще не убежден, что мой дизайн - лучший дизайн. Возможно, мне действительно нужно использовать метод экземпляра? – flornquake

+0

@jonrsharpe Я ищу способы избежать этой проблемы со стороны класса, а не со стороны вызывающего, если это возможно. – flornquake

ответ

2

Наследование явно означает знание базового класса. @staticmethod не знает о классе он «прикреплен» к (и, следовательно, они раньше, не сейчас-дни, назвали его «несвязанным;» теперь технически @staticmethod не метод вообще,. Это функция

Но @classmethod полностью осведомлен о классе, к которому он привязан: технически это не функция, а метод.

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

Нетехнически говорящие @classmethod s являются 'bounded';@staticmethod с НЕ.

Если бы кто-нибудь спросил меня, я бы предложил имя @staticfunction для @staticmethod.

+0

Поскольку вы упомянули более одного раза, можете ли вы описать техническую разницу между методом и функцией? – ali

Смежные вопросы