En Python, ¿cuándo debería usar una función en lugar de un método?

El Zen de Python afirma que solo debería haber una forma de hacer las cosas, pero con frecuencia me encuentro con el problema de decidir cuándo usar una función y cuándo usar un método.

Tomemos un ejemplo trivial: un objeto ChessBoard. Digamos que necesitamos alguna forma de obtener todos los movimientos legales de King disponibles en el tablero. ¿Escribimos ChessBoard.get_king_moves () o get_king_moves (chess_board)?

Aquí hay algunas preguntas relacionadas que miré:

¿Por qué Python usa 'métodos mágicos'? ¿Hay alguna razón por la cual las cadenas de Python no tienen un método de longitud de cadena?

Las respuestas que obtuve no fueron concluyentes:

¿Por qué Python utiliza métodos para alguna funcionalidad (por ejemplo, list.index ()) pero funciones para otra (por ejemplo, len (list))?

La razón principal es la historia. Se utilizaron funciones para aquellas operaciones que eran genéricas para un grupo de tipos y que estaban destinadas a funcionar incluso para objetos que no tenían ningún método (por ejemplo, tuplas). También es conveniente tener una función que se pueda aplicar fácilmente a una colección amorfa de objetos cuando use las características funcionales de Python (map (), apply () et al).

De hecho, implementar len (), max (), min () como una función incorporada es en realidad menos código que implementarlos como métodos para cada tipo. Uno puede discutir sobre casos individuales, pero es parte de Python, y es demasiado tarde para hacer cambios tan fundamentales ahora. Las funciones deben permanecer para evitar la rotura masiva del código.

unque es interesante, lo anterior realmente no dice mucho sobre qué estrategia adoptar.

Esta es una de las razones: con métodos personalizados, los desarrolladores podrían elegir un nombre de método diferente, como getLength (), length (), getlength () o cualquier otro. Python impone una nomenclatura estricta para que se pueda usar la función común len ().

Ligeramente más interesante. Mi opinión es que las funciones son, en cierto sentido, la versión Pythonic de las interfaces.

Por último,del mismo Guido:

Hablar sobre las habilidades / interfaces me hizo pensar en algunos de nuestros nombres de métodos especiales "deshonestos". En la Referencia del lenguaje, dice: "Una clase puede implementar ciertas operaciones que son invocadas por una sintaxis especial (como operaciones aritméticas o suscripciones y segmentación) definiendo métodos con nombres especiales". Pero hay todos estos métodos con nombres especiales como__len__ o__unicode__ que parecen proporcionarse para el beneficio de las funciones integradas, en lugar de para el soporte de la sintaxis. Presumiblemente en un Python basado en interfaz, estos métodos se convertirían en métodos nombrados regularmente en un ABC, de modo que__len__ se convertirí

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

A pesar de pensarlo un poco más, no veo por qué todosas operaciones @syntactic no solo invocarían el método apropiado normalmente nombrado en un ABC específico. "< ", por ejemplo, probablemente invocaría"object.lessthan" (o quizás "comparable.lessthan "). Entonces, otro beneficio sería la capacidad de alejar a Python de esta rareza de nombre destrozado, lo que me parece una mejora de HCI.

Hm. No estoy seguro de estar de acuerdo (figura que: -).

Hay dos partes de "justificación de Python" que me gustaría explicar primero.

En primer lugar, elegí len (x) sobre x.len () por razones de HCI def __len__() vino mucho más tarde). En realidad, hay dos razones entrelazadas, ambas HCI:

(a) Para algunas operaciones, la notación de prefijo solo se lee mejor que postfix: las operaciones de prefijo (¡y de infijo!) tienen una larga tradición en matemáticas que le gustan las anotaciones en las que las imágenes ayudan al matemático a pensar en un problema. Compare lo fácil con que reescribimos una fórmula comox*(a+b) dentrox*a + x*b a la torpeza de hacer lo mismo usando una notación OO sin formato.

(b) Cuando leo el código que dicelen(x) I sabe que está pidiendo la duración de algo. Esto me dice dos cosas: el resultado es un número entero y el argumento es algún tipo de contenedor. Por el contrario, cuando leox.len(), Ya tengo que saber quex es algún tipo de contenedor que implementa una interfaz o hereda de una clase que tiene un estándarlen(). Sea testigo de la confusión que ocasionalmente tenemos cuando una clase que no está implementando un mapeo tiene unget() okeys() método, o algo que no es un archivo tiene unwrite() método.

Dice lo mismo de otra manera, veo 'len' como un @ incorporaoperació. Odiaría perder eso. No puedo decir con certeza si quisiste decir eso o no, pero 'def len (self): ...' ciertamente parece que quieres degradarlo a un método ordinario. Estoy fuertemente -1 en eso.

La segunda razón de Python que prometí explicar es la razón por la que elegí métodos especiales para buscar__special__ y no simplementespecial. Estaba anticipando muchas operaciones que las clases querrían anular, algunas estándar (por ejemplo,__add__ o__getitem__), algunos no tan estándar (por ejemplo, pickle's__reduce__ durante mucho tiempo no tenía soporte en el código C en absoluto). No quería que estas operaciones especiales usaran nombres de métodos ordinarios, porque las clases preexistentes, o clases escritas por usuarios sin una memoria enciclopédica para todos los métodos especiales, podrían definir accidentalmente operaciones que no pretendían implementar , con consecuencias posiblemente desastrosas. Ivan Krstić explicó esto más conciso en su mensaje, que llegó después de haber escrito todo esto.

- --Guido van Rossum (página de inicio:http: //www.python.org/~guido)

Mi comprensión de esto es que, en ciertos casos, la notación de prefijo tiene más sentido (es decir, Duck.quack tiene más sentido que quack (Duck) desde un punto de vista lingüístico) y, de nuevo, las funciones permiten "interfaces".

En tal caso, supongo que implementaría get_king_moves basándose únicamente en el primer punto de Guido. Pero eso todavía deja muchas preguntas abiertas con respecto a, por ejemplo, implementar una clase de pila y cola con métodos push y pop similares, ¿deberían ser funciones o métodos? (aquí adivinaría funciones, porque realmente quiero señalar una interfaz push-pop)

TLDR: ¿Alguien puede explicar cuál debería ser la estrategia para decidir cuándo usar las funciones frente a los métodos?

Respuestas a la pregunta(6)

Su respuesta a la pregunta