дизайн класса Python (статический метод против метода)

Что может быть лучше для методов, которым не нужна какая-либо передаваемая информация (экземпляр объекта или класс), потому что, например, они просто выполняют простое преобразование. @staticmethod или метод?

class Foo(object):
    def __init__(self, trees):
        self.money = Foo.trees2money(trees)

    @staticmethod
    def trees2money(trees):
        return trees * 1.337

class Quu(object):
    def __init__(self, trees):
        self.money = self.trees2money(trees)

    def trees2money(self, trees):
        return trees * 1.337

person Tammo B.    schedule 06.03.2011    source источник
comment
статические методы бесполезны в python, вы можете сделать то же самое с обычной функцией...   -  person Srinivas Reddy Thatiparthy    schedule 06.03.2011
comment
@Srinivas Reddy Thatiparthy: Не совсем так, вы добавляете метод в определенное пространство имен. Хотя я не думаю, что его (статический метод) следует часто использовать.   -  person extraneon    schedule 06.03.2011
comment
@extranon: Ну, в принципе бесполезно. Пространство имен иногда может быть полезным, но когда вы переходите с другого языка, вы должны игнорировать @staticmethod, потому что все, что вы хотите сделать с помощью статического метода, вероятно, должно быть сделано с помощью свободной функции или метода класса.   -  person    schedule 06.03.2011
comment
Этот конкретный пример выглядит так, как будто его лучше всего поместить в функцию уровня модуля, а не в класс, как заявляет Рош Оксюморон в своем ответе.   -  person dappawit    schedule 06.03.2011


Ответы (3)


Выбор типа метода зависит от других факторов.

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

В этом первом случае вы часто использовали бы либо обычный метод (это когда метод относится к экземплярам, ​​а не к классу), либо classmethod (когда метод относится к классу, например, это альтернативный конструктор, метод для обнаружение признаков класса и т. д.). В обоих случаях вы можете использовать staticmethod вместо этого, если метод не использует информацию из класса/экземпляра, но вы ничего не получаете от этого. И это также нарушило бы вашу способность делать cls.method(instance, *args), что уже слишком много, чтобы ничего не получить.

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

Особым случаем являются приватные методы — тогда вы можете захотеть использовать метод, даже если он на самом деле не связан с классом, приватные методы не являются частью интерфейса, поэтому не имеет значения, где вы их поместите. Использование staticmethod по-прежнему не дает вам многого, но и нет никаких причин не использовать его.

На самом деле есть один случай, когда staticmethod очень полезен — когда вы помещаете в класс внешние функции (или другие объекты).

class Foo(object):
     trees2money = staticmethod(calculators.trees2money)
     foo = staticmethod(calculators.bar)

Но когда у вас есть статическое определение класса, это не очень хорошо, потому что вместо этого вы всегда можете сделать следующее.

class Foo(object):
     def trees2money(self, trees):
         """Calculator for trees2money, you can override when subclassing"""
         return calculators.trees2money(trees)
     @property
     def foo(self):
         """The foo of the object"""
         return calculators.bar

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

person Rosh Oxymoron    schedule 06.03.2011
comment
+1: хорошо сказано. Я также обнаружил, что единственный раз, когда я использую статические методы, — это частные служебные функции, используемые внутри класса. И даже это очень редко. - person dappawit; 06.03.2011

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

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

person Mike    schedule 06.03.2011
comment
@Mike: Если метод не использует состояние, разве наличие или отсутствие состояния не имеет значения? Не знаю слишком много теории OOPS, я что-то упустил? - person Vamana; 06.03.2011
comment
Метод представляет поведение класса, тогда как состояние представляет данные. Мы стремимся к хорошей симметрии, когда в классах есть и то, и другое. Если метод не использует состояние класса, то класс может быть не очень связным. - person Mike; 06.03.2011
comment
Если у класса нет состояния, я мог бы рассмотреть возможность превращения его методов в бесплатные функции. Моя мысль заключалась в том, что если нет состояния для чтения/изменения, зачем вообще иметь класс? (Очевидно, что это не относится к таким языкам, как Java, где каждая функция должна быть частью класса.) - person dappawit; 06.03.2011
comment
@dappawit Если я когда-нибудь подумаю об использовании глобальной функции в объектно-ориентированном языке, я сильно подозреваю, что есть лучшее место (класс) для размещения этой функции. - person Mike; 06.03.2011
comment
Нам просто нужно согласиться, чтобы не согласиться. - person dappawit; 07.03.2011
comment
Однако на самом деле в Python нет глобальных функций. Функции, которые вы пишете, являются глобальными только для модуля. На самом деле это не очень глобально. - person kindall; 07.03.2011
comment
Я нахожу неограниченные функции глобальными, но это мое понимание. - person Mike; 07.03.2011

Любые методы, которые не используют имена из пространства имён объекта, должны быть классовыми методами, а те, которые не используют имена из пространства имён класса, либо — staticmetdods, либо вообще выноситься на уровень модуля как функции.

Когда используется метод фактического объекта, ожидаемое поведение заключается в том, что будут доступны некоторые другие атрибуты/методы того же объекта.

person vonPetrushev    schedule 06.03.2011