Дзен Python утверждает, что должен быть только один способ делать что-то, но часто я сталкиваюсь с проблемой решения, когда использовать функцию, а когда использовать метод.
Возьмем тривиальный пример - объект ChessBoard. Допустим, нам нужен какой-то способ получить все доступные ходы короля на доске. Мы пишем ChessBoard.get_king_moves () или get_king_moves (chess_board)?
Вот несколько вопросов, которые я рассмотрел:
- Почему Python использует «волшебные методы»?
- Есть ли причина, по которой строки Python не имеют метода длины строки?
Ответы, которые я получил, были в основном неубедительными:
Почему Python использует методы для некоторых функций (например, list.index ()), но функции для других (например, len (list))?
Основная причина - история. Функции использовались для тех операций, которые были общими для группы типов и которые были предназначены для работы даже с объектами, у которых вообще не было методов (например, кортежи). Также удобно иметь функцию, которую можно легко применить к аморфной коллекции объектов, когда вы используете функциональные возможности 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)
я знаю, что он запрашивает длину чего-то. Это говорит мне о двух вещах: результат - целое число, а аргумент - это своего рода контейнер. Напротив, когда я читаюx.len()
, я уже должен знать, чтоx
это какой-то контейнер, реализующий интерфейс или наследующий от класса, имеющего стандартlen()
. Свидетель путаницы мы иногда есть , когда класс , который не реализует отображение имеетget()
илиkeys()
метод, или то , что не файл имеетwrite()
метод.Говоря то же самое по-другому, я рассматриваю len как встроенную операцию . Я бы не хотел это потерять. Я не могу точно сказать, имели ли вы это в виду или нет, но 'def len (self): ...' определенно звучит так, как будто вы хотите понизить его до обычного метода. Я категорически против.
Второе обоснование Python, которое я обещал объяснить, - это причина, по которой я выбрал для поиска специальные методы,
__special__
а не простоspecial
. Я ожидал множества операций, которые классы могут захотеть переопределить, некоторые стандартные (например,__add__
или__getitem__
), некоторые не такие стандартные (например, pickle__reduce__
долгое время вообще не поддерживался в коде C). Я не хотел, чтобы в этих специальных операциях использовались обычные имена методов, потому что тогда уже существующие классы или классы, написанные пользователями без энциклопедической памяти для всех специальных методов, будут склонны случайно определять операции, которые они не собирались реализовать. , с возможными катастрофическими последствиями. Иван Крстич более кратко объяснил это в своем сообщении, которое пришло после того, как я все это написал.- --Гидо ван Россум (домашняя страница: http://www.python.org/~guido/ )
Насколько я понимаю, в некоторых случаях префиксная нотация просто имеет больше смысла (например, Duck.quack имеет больше смысла, чем quack (Duck) с лингвистической точки зрения). И снова функции позволяют использовать «интерфейсы».
В таком случае я предполагаю реализовать get_king_moves исключительно на основе первого пункта Гвидо. Но это по-прежнему оставляет много открытых вопросов, касающихся, скажем, реализации класса стека и очереди с аналогичными методами push и pop - должны ли они быть функциями или методами? (здесь я бы угадал функции, потому что я действительно хочу сигнализировать об интерфейсе push-pop)
TL; DR: Может ли кто-нибудь объяснить, какой должна быть стратегия принятия решения, когда использовать функции или методы?
X.frob
илиX.__frob__
и автономныйfrob
.