Функциональный модуль против staticmethod против classmethod в Против нет оформителей: какой фразеологизм является более подходящие для Python?


Я разработчик Java, который играл вокруг С Python и выключается. Я недавно наткнулся на в этой статье который упоминает распространенные ошибки Java-программистов, когда они берут Python. Первое попалось мне на глаза:

статический метод в Java не преобразуется в Python classmethod. О, Конечно, это приводит к более или менее одинаковому эффекту, но цель classmethod на самом деле сделать что-то, что обычно даже невозможно в Java (например наследование конструктора не по умолчанию). Идиоматический перевод статического метода Java обычно является функцией уровня модуля, а не classmethod или staticmethod. (И статические конечные поля должны переводиться в константы уровня модуля.)

это не большая проблема с производительностью, но программист Python, который должен работать с кодом Java-идиомы, как это, будет довольно раздражен, набрав Foo.Foo.что-то вроде того, когда это должно быть просто фу.someFunction. Но обратите внимание, что вызов classmethod включает в себя дополнительное выделение памяти, которое не вызывает staticmethod или функцию.

О, и все эти Фу.Бар.Цепочки атрибутов Baz также не приходят бесплатно. В Java эти пунктирные имена просматриваются компилятором, поэтому во время выполнения действительно не имеет значения, сколько из них у вас есть. В Python поиск происходит во время выполнения,поэтому каждая точка считается. (Помните, что в Python "плоский лучше, чем вложенный", хотя это больше связано с "количеством читаемости" и " простой лучше, чем комплекс", чем быть о производительности.)

Я нашел это немного странно, потому что документация staticmethod говорит:

статические методы в Python аналогичны тем, которые находятся в Java или C++. Также см. classmethod() для варианта, который полезен для создания альтернативных конструкторов классов.

еще более загадочным является то, что этот код:

class A:
    def foo(x):
        print(x)
A.foo(5)

Не удается, как ожидалось в Python 2.7.3, но отлично работает в 3.2.3 (хотя вы не можете вызвать метод на экземпляре, только на классе.)

таким образом, есть три способа реализации статических методов (четыре, если вы считаете, используя classmethod), каждый с тонкими различиями, один из них, казалось бы, недокументированный. Это, кажется, расходится с мантрой Python или должен быть один-и, желательно, только один-очевидный способ сделать это. что фразеологизм является наиболее подходящие для Python? Каковы плюсы и минусы каждого?

вот что Я понимаю до сих пор:

модуль функции:

  • избегает Foo.Foo.Ф (проблемы)
  • загрязняет пространство имен модуля больше, чем альтернативы
  • наследства

staticmethod:

  • сохраняет функции, связанные с классом внутри класса и вне пространства имен модуля.
  • позволяет вызывать функцию на экземплярах класс.
  • подклассы могут переопределить этот метод.

classmethod:

  • идентичен staticmethod, но также передает класс в качестве первого аргумента.

обычный метод (только Python 3):

  • идентично staticmethod, но не может вызвать метод на экземплярах класса.

Я слишком много думаю об этом? Это не проблема? Пожалуйста помогите!

4 52

4 ответа:

самый простой способ думать об этом-думать в терминах того, какой тип объекта нужен методу для выполнения своей работы. Если ваш метод нуждается в доступе к экземпляру, сделайте его обычным методом. Если ему нужен доступ к классу, сделайте его classmethod. Если ему не нужен доступ к классу или экземпляру, сделайте его функцией. Редко возникает необходимость сделать что-то staticmethod, но если вы обнаружите, что хотите, чтобы функция была "сгруппирована" с классом (например, так это может быть переопределено) даже если он не нуждается в доступе к классу, я думаю, вы могли бы сделать его staticmethod.

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

отличный ответ от BrenBarn, но я бы изменил 'если ему не нужен доступ к классу или экземпляру, сделайте его функцией' to:

'Если он не нужен доступ к классу или экземпляру...но и тематически связанные с классом (типичный пример: вспомогательные функции и функции преобразования, используемые другими методами класса или используемые альтернативными конструкторами), затем используйте staticmethod

еще сделать это a функция модули

это не совсем ответ, а скорее пространный комментарий:

еще более загадочным является то, что этот код:

        class A:
            def foo(x):
                print(x)
        A.foo(5)

не работает, как ожидалось в Python 2.7.3, но отлично работает в 3.2.3 (хотя вы не можете вызвать метод на экземпляре, только на классе.)

я постараюсь объяснить, что здесь происходит.

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

то, что вы определяете здесь, является методом, но с первым (и единственным) параметром без имени self, а x. Конечно, вы можете вызвать метод экземпляра A, но вы должны будете назвать это так:

A().foo()

или

a = A()
a.foo()

поэтому экземпляр передается функции в качестве первого аргумента.

возможность вызова регулярных методов через класс всегда была и работает на

a = A()
A.foo(a)

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

пока это пример A все ок. Давая ему что-то еще, ИМО злоупотребляет протоколом, и, следовательно, разница между Py2 и Py3:

В Py2, A.foo преобразуется в несвязанный метод и поэтому требует, чтобы его первый аргумент был экземпляр класса, в котором он "живет". Вызов его с чем-то другим потерпит неудачу.

в Py3 эта проверка была отброшена и A.foo это просто исходный объект функции. Так что вы можете назвать это со всем, как первый аргумент, но я бы не стал этого делать. Первый параметр метода всегда должен иметь имя self и семантика self.

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

основное преимущество определений функций заключается в том, что пользователь может импортировать их определяющий файл с помощью ключевого слова "as". Это позволяет пользователю вызывать функции таким же образом, как они вызывали бы функцию в numpy или matplotlib.

один из недостатки Python заключается в том, что имена не могут быть защищены от дальнейшего присвоения. Однако, если" import numpy as np "появляется в верхней части ноутбука, это сильный намек на то, что" np " не следует использовать в качестве общего имени переменной. Вы можете сделать то же самое с именами классов, очевидно, но знакомство с пользователем имеет большое значение.

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

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