Zen of Python утверждает, что должен быть только один способ сделать что-то, но часто я сталкиваюсь с проблемой принятия решения, когда использовать функцию, а не когда использовать метод.
Давайте возьмем тривиальныйпример - объект ChessBoard.Допустим, нам нужен какой-то способ получить все легальные ходы Кинга, доступные на доске.Пишем ли мы ChessBoard.get_king_moves () или get_king_moves (chess_board)?
Вот несколько связанных вопросов, на которые я посмотрел:
Ответы, которые я получил, были в значительной степени неубедительными:
Почему Python использует методы для некоторых функций (например, list.index ()), но функции для других (например, len (список))?
Основная причина - история.Функции использовались для тех операций, которые были общими для группы типов и предназначались для работы даже с объектами, у которых вообще не было методов (например, кортежей).Также удобно иметь функцию, которую можно легко применить к аморфному набору объектов, когда вы используете функциональные возможности Python (map (), apply () и др.).
Фактически, реализация len(), max (), min () как встроенная функция на самом деле меньше кода, чем их реализация в качестве методов для каждого типа.Можно спорить об отдельных случаях, но это часть Python, и уже слишком поздно вносить такие фундаментальные изменения.Функции должны оставаться, чтобы избежать массивного взлома кода.
Хотя это интересно, приведенное выше не особо говорит о том, какую стратегию принять.
Это одиниз причин - с пользовательскими методами разработчики могут свободно выбирать другое имя метода, например getLength (), length (), getlength () или что-то еще.Python обеспечивает строгое именование, так что можно использовать общую функцию len ().
Чуть интереснее.Я полагаю, что в некотором смысле функции - это Python-версия интерфейсов.
Наконец, от самого Гвидо :
Talkingнасчет Способностей / Интерфейсов заставил меня задуматься о некоторых из наших "мошеннических" имен специальных методов.В справочнике по языку говорится: «Класс может реализовывать определенные операции, которые вызываются специальным синтаксисом (например, арифметические операции или подписывание и разрезание) путем определения методов со специальными именами».Но есть все эти методы со специальными именами, такими как __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
, с неуклюжостью doing то же самое, используя необработанную запись OO.
(б) Когда я читаю код, который говорит len(x)
Я знаю , что он просит
длина чего-то. Это говорит мне о двух вещах: результат
целое число, а аргумент является своего рода контейнером. Иначе,
когда я читаю x.len()
, я уже должен знать, что x
является чем-то вроде
контейнер, реализующий интерфейс или наследующий от класса,
имеет стандарт len()
. Станьте свидетелем путаницы, которую мы иногда имеем
класс, который не реализует отображение, имеет get()
или keys()
метод, или что-то, что не является файлом, имеет метод write()
.
Говоря то же самое по-другому, я вижу 'len' как встроенный
работа . Я бы не хотел потерять это. Я не могу точно сказать, имели ли вы в виду это или нет, но «def len (self): ...» звучит как вы
хочу понизить его до обычного метода. Я сильно -1 на этом.
Вторая часть объяснения Python, которое я обещал объяснить, является причиной
почему я выбрал специальные методы, чтобы смотреть __special__
, а не просто
special
. Я ожидал много операций, которые могли бы хотеть классы
переопределить, некоторые стандартные (например, __add__
или __getitem__
), некоторые не очень
стандарт (например, __reduce__
от pickle долгое время не имел поддержки в C
код вообще). Я не хотел, чтобы эти специальные операции использовали обычные
имена методов, потому что тогда уже существующие классы или классы, написанные
пользователи без энциклопедической памяти для всех специальных методов,
будет склонен случайно определить операции, которые они не хотели
осуществлять с возможными катастрофическими последствиями. Иван Крстич
объяснил это более кратко в своем сообщении, которое пришло после того, как я
записал все это.
-
- Гидо ван Россум (домашняя страница: http://www.python.org/~guido/)
Мое понимание этого заключается в том, что в некоторых случаях префиксная нотация имеет больше смысла (т. Е. Duck.quack имеет больше смысла, чем кряк (Duck) с лингвистической точки зрения.) И опять же, функции допускают «интерфейсы».
В таком случае, я бы предположил реализовать get_king_moves, основываясь исключительно на первом пункте Гвидо. Но это все еще оставляет много открытых вопросов относительно, скажем, реализации стека и класса очереди с подобными методами push и pop - должны ли они быть функциями или методами? (здесь я бы угадал функции, потому что я действительно хочу сигнализировать об интерфейсе push-pop)
TLDR: Может ли кто-нибудь объяснить, какой должна быть стратегия для решения, когда использовать функции и методы?