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

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

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

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

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

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

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

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

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

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

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

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

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

: {{ 1}} ...
def len (self): 
raise NotImplemented 
 

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

Хм. Я не уверен, что согласен (подумайте, что :-).

Есть две части "обоснования Python", которые я хотел бы сначала объяснить .

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

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

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

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

Второе обоснование Python, которое я обещал объяснить, - это причина того, почему я выбрал специальные методы для поиска __ special __ , а не просто special . Я ожидал множество операций, которые классам может понадобиться переопределить , некоторые стандартные (например, __ add __ или __ getitem __ ), некоторые не такие стандартные (например, __ reduce __ pickle долгое время вообще не поддерживался в коде C ). Я не хотел, чтобы в этих специальных операциях использовались обычные имена методов , потому что тогда уже существующие классы или классы, написанные пользователями без энциклопедической памяти для всех специальных методов, {{1} } может случайно определить операции, которые они не собирались реализовывать, что может иметь катастрофические последствия. Иван Крстич более кратко объяснил это в своем сообщении, которое пришло после того, как я все это написал.

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

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

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

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

107
задан Ceasar Bautista 16 November 2017 в 00:01
поделиться