В Python, когда я должен использовать функцию вместо метода?

Zen of Python утверждает, что должен быть только один способ сделать что-то, но часто я сталкиваюсь с проблемой принятия решения, когда использовать функцию, а не когда использовать метод.

Давайте возьмем тривиальный пример - объект ChessBoard. Допустим, нам нужен какой-то способ получить все легальные ходы Кинга, доступные на доске. Пишем ли мы ChessBoard.get_king_moves () или get_king_moves (chess_board)?

Вот некоторые связанные вопросы, на которые я смотрел:

Почему Python использует «магические методы»?Есть ли причина, по которой строки Python не имеют метода длины строки?

Ответы, которые я получил, были в основном неубедительными:

Почему Python использует методы для некоторых функций (например, list.index ()), а функции для других (например, len (список))?

Основная причина - история. Функции использовались для тех операций, которые были общими для группы типов и предназначались для работы даже с объектами, у которых вообще не было методов (например, кортежей). Также удобно иметь функцию, которую можно легко применить к аморфному набору объектов, когда вы используете функциональные возможности Python (map (), apply () и др.).

Фактически, реализация len (), max (), min () как встроенной функции на самом деле меньше кода, чем их реализация в качестве методов для каждого типа. Можно спорить об отдельных случаях, но это часть Python, и уже слишком поздно вносить такие фундаментальные изменения. Функции должны оставаться, чтобы избежать массивного взлома кода.

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

Это одна из причин - при использовании пользовательских методов разработчики могут свободно выбирать другое имя метода, например, getLength (), length (), getlength () или что-то еще. Python обеспечивает строгое именование, чтобы можно было использовать общую функцию len ().

Чуть интереснее. Я считаю, что в некотором смысле функции - это Pythonic-версия интерфейсов.

И, наконец,от самого Гвидо:

Разговор о Способностях / Интерфейсах заставил меня задуматься о некоторых из наших «мошеннических» имен специальных методов. В справочнике по языку говорится: «Класс может реализовывать определенные операции, которые вызываются специальным синтаксисом (например, арифметические операции или подписывание и разрезание) путем определения методов со специальными именами». Но есть все эти методы со специальными именами, такими как__len__ или же__unicode__ которые, как представляется, предоставляются для удобства встроенных функций, а не для поддержки синтаксиса. Предположительно в основанном на интерфейсе Python эти методы превратятся в методы с регулярными именами на ABC, так что__len__ станет

class container:
  ...
  def len(self):
    raise NotImplemented

Хотя, думая об этом еще немного, я не понимаю, почему всеСинтаксические операции не будут просто вызывать соответствующий метод с нормальным именем на определенной ABC. "<«Например, предположительно будет вызывать»object.lessthan"(или возможно"comparable.lessthan"). Таким образом, еще одним преимуществом будет возможность отучить Python от этой странности с искаженными именами, что мне кажется улучшением HCI.

Гектометр Я не уверен, что согласен (думаю, что :-).

Есть два бита «Обоснования Python», которые я хотел бы объяснить в первую очередь.

Прежде всего, я выбрал len (x) вместо x.len () по причинам HCI (def __len__() пришел намного позже). На самом деле есть две взаимосвязанные причины, обе - HCI:

(a) Для некоторых операций префиксная нотация читается лучше, чем постфиксная - у префиксных (и инфиксных!) операций есть давняя традиция в математике, которой нравятся нотации, когда визуальные элементы помогают математику думать о проблеме. Сравните то, с чем мы переписываем формулуx*(a+b) вx*a + x*b неуклюже делать то же самое, используя необработанную запись ОО.

(б) Когда я читаю код, который говоритlen(x) I знать что он просит длины чего-то. Это говорит мне о двух вещах: результат - целое число, а аргумент - своего рода контейнер. Наоборот, когда я читаюx.len()Я должен уже знать, чтоx это какой-то контейнер, реализующий интерфейс или наследующий от класса, который имеет стандартlen(), Обратите внимание на путаницу, которую мы иногда испытываем, когда класс, не реализующий отображение, имеетget() или жеkeys() метод, или что-то, что не является файлом, имеетwrite() метод.

Говоря то же самое по-другому, я вижу «лен» как встроенныйоперация, Я бы не хотел потерять это. Я не могу с уверенностью сказать, имели ли вы в виду это или нет, но «def len (self): ...», безусловно, звучит так, будто вы хотите понизить его до обычного метода. Я сильно -1 на этом.

Второе объяснение Python, которое я обещал объяснить, - причина, по которой я выбрал специальные методы__special__ а не простоspecial, Я ожидал много операций, которые классы могут захотеть переопределить, некоторые стандартные (например,__add__ или же__getitem__), некоторые не очень стандартные (например, маринованные__reduce__ долгое время вообще не имел поддержки в С-коде). Я не хотел, чтобы эти специальные операции использовали обычные имена методов, потому что тогда уже существующие классы или классы, написанные пользователями без энциклопедической памяти для всех специальных методов, могли бы случайно определить операции, которые они не намеревались реализовать с возможными катастрофическими последствиями. Иван Крстич объяснил это более кратко в своем сообщении, которое пришло после того, как я написал все это.

- Гвидо ван Россум (домашняя страница:http://www.python.org/~guido/)

Насколько я понимаю, в некоторых случаях префиксная нотация имеет больше смысла (т. Е. Duck.quack имеет больше смысла, чем кряк (Duck) с лингвистической точки зрения), и опять же, функции допускают «интерфейсы».

В таком случае я предполагаю реализовать get_king_moves, основываясь исключительно на первом пункте Гвидо. Но это все еще оставляет много открытых вопросов относительно, скажем, реализации стека и класса очереди с подобными методами push и pop - должны ли они быть функциями или методами? (здесь я бы угадал функции, потому что я действительно хочу сигнализировать интерфейс push-pop)

TLDR: Может ли кто-нибудь объяснить, какой должна быть стратегия для решения, когда использовать функции и методы?

Ответы на вопрос(6)

Ваш ответ на вопрос