Pregunta ¿Por qué Python tiene una función de formato y un método de formateo?


los format función en edificios internos parece ser un subconjunto de la str.format método utilizado específicamente para el caso de un formato de un solo objeto.

p.ej.

>>> format(13, 'x')
'd'

es aparentemente preferido por encima

>>> '{0:x}'.format(13)
'd'

e IMO se ve mejor, pero ¿por qué no usar str.format en todos los casos para hacer las cosas más simples? Ambos fueron introducidos en 2.6 entonces debe haber una buena razón para tener ambas cosas a la vez, ¿qué es?

Editar: Estaba preguntando sobre str.format y format, no por qué no tenemos una (13).format


32
2018-05-22 04:33


origen


Respuestas:


creo format y str.format hacer cosas diferentes Aunque podrías usar str.format para ambos, tiene sentido tener versiones separadas.

El nivel superior format la función es parte del nuevo "protocolo de formateo" que todos los objetos admiten. Simplemente llama al __format__ método del objeto que se pasa, y devuelve una cadena. Esta es una tarea de bajo nivel, y el estilo de Python suele tener funciones integradas para ellos. La respuesta de Paulo Scardine explica algunas de las razones para esto, pero no creo que realmente aborde las diferencias entre lo que format y str.format hacer.

los str.format método es un poco más de alto nivel, y también un poco más complejo. No solo puede formatear múltiples objetos en un solo resultado, sino que también puede reordenar, repetir, indexar y hacer otras varias transformaciones en los objetos. No pienses solo en "{}".format(obj). str.format está realmente diseñado para más sobre tareas complicadas, como estas:

"{1} {0} {1!r}".format(obj0, obj1) # reorders, repeats, and and calls repr on obj1
"{0.value:.{0.precision}f}".format(obj) # uses attrs of obj for value and format spec
"{obj[name]}".format(obj=my_dict) # takes argument by keyword, and does an item lookup

Para el formateo de bajo nivel de cada elemento, str.format depende de la misma maquinaria del protocolo de formato, por lo que puede enfocar sus propios esfuerzos en las cosas de nivel superior. Dudo que en realidad llame a la builtin format, en lugar de sus argumentos ' __format__ métodos, pero eso es un detalle de implementación.

Mientras ("{"+format_code+"}").format(obj) está garantizado para dar los mismos resultados que format(obj, format_code), Sospecho que este último será un poco más rápido, ya que no es necesario analizar la cadena de formato para comprobar si hay algo complicado. Sin embargo, la sobrecarga puede perderse en el ruido en un programa real.

Cuando se trata de uso (incluidos ejemplos sobre el desbordamiento de la pila), es posible que vea más str.format utilizar simplemente porque algunos programadores no saben sobre format, que es a la vez nuevo y bastante oscuro. En contraste, es difícil evitar str.format (a menos que haya decidido quedarse con el % operador para todo su formato). Entonces, la facilidad (para usted y sus compañeros programadores) de entender una str.format la llamada puede superar cualquier consideración de rendimiento.


6
2018-05-22 22:37



tldr;  format solo llamadas obj.__format__ y es utilizado por el str.format método que hace aún más cosas de nivel superior. Para el nivel inferior, tiene sentido enseñarle a un objeto cómo formatearse.

Es solo azúcar sintáctico

El hecho de que esta función comparte el nombre y la especificación de formato con str.format puede ser engañoso La existencia de str.format es fácil de explicar: hace una interpolación compleja de cadenas (reemplazando a las antiguas % operador); formatpuede formatear un solo objeto como cadena, el subconjunto más pequeño de str.format especificación. Entonces, ¿por qué necesitamos format?

los format la función es una alternativa a la obj.format('fmt') constructo encontrado en algunos OO idiomas. Esta decisión es consistente con la justificación de len (sobre por qué Python usa una función len(x) en lugar de una propiedad x.length me gusta Javascript o Ruby).

Cuando un lenguaje adopta el obj.format('fmt') construir (o obj.length, obj.toString y así sucesivamente), se impide que las clases tengan un atributo llamado format (o length, toString, ya entendiste la idea); de lo contrario, sombrearía el método estándar del idioma. En este caso, los diseñadores del lenguaje están cargando la tarea de evitar los conflictos de nombres en el programador.

Python es muy aficionado a la PoLA y adoptó el __dunder__ (doble subrayado) convención para integradas con el fin de minimizar la posibilidad de conflictos entre los atributos definidos por el usuario y el lenguaje incorporado. Asi que obj.format('fmt') se convierte obj.__format__('fmt')y, por supuesto, puedes llamar obj.__format__('fmt') en lugar de format(obj, 'fmt') (de la misma manera que puedes llamar obj.__len__() en lugar de len(obj))

Usando tu ejemplo:

>>> '{0:x}'.format(13)
'd'
>>> (13).__format__('x')
'd'
>>> format(13, 'x')
'd'

¿Cuál es más limpio y más fácil de escribir? El diseño de Python es muy pragmático, no solo es más limpio sino que está bien alineado con el de Python. pato-mecanografiado acercarse a OO y le da libertad a los diseñadores de lenguaje para cambiar / extender la implementación subyacente sin romper el código heredado.

los PEP 3101 introdujo el nuevo str.format método y format incorporado sin ningún comentario sobre la justificación de la format función, pero la implementación es obviamente solo azúcar sintáctica:

def format(value, format_spec):
    return value.__format__(format_spec)

Y aquí descanso mi caso.

¿Qué dijo Guido al respecto (o es oficial?)

Citando el mismo BDFL acerca de len:

Primero que nada, elegí len(x) encima x.len() para HCI razones (def __len__() llegó mucho más tarde). En realidad, hay dos razones entrelazadas, ambas HCI:

(a) Para algunas operaciones, la notación de prefijo simplemente lee mejor que postfix - las operaciones de prefijo (¡e infijo!) tienen una larga tradición en matemáticas a las que les gustan las notaciones donde los elementos visuales ayudan al matemático a pensar en un problema. Compare lo fácil con lo que reescribimos una fórmula como x*(a+b) dentro x*a + x*b a la torpeza de hacer lo mismo usando una notación OO cruda.

(b) Cuando leo el código que dice len(x) Sé que está pidiendo la longitud 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 leo x.len(), Tengo que saberlo x es algún tipo de contenedor implementando una interfaz o heredando de una clase que tiene un estándar len(). Sea testigo de la confusión que ocasionalmente tenemos cuando una clase que no está implementando un mapeo tiene una get() o keys() método, o algo que no es un archivo tiene una write() método.

Diciendo lo mismo de otra manera, veo 'len'Como una operación incorporada. Odiaría perder eso. / ... /

fuente: pyfaq@effbot.org (publicación original aquí tiene también la pregunta original que Guido estaba respondiendo). Abarnert sugiere también:

Hay un razonamiento adicional sobre Len en el Preguntas frecuentes sobre diseño e historia. Aunque no es tan completa ni tan buena como una respuesta, es indiscutiblemente oficial. - abarnert

¿Es esto una preocupación práctica o simplemente una sintaxis nimia?

Esta es una preocupación muy práctica y real en idiomas como Python, Rubí o Javascript porque en los lenguajes de tipado dinámico cualquier objeto mutable es efectivamente un espacio de nombres, y el concepto de métodos o atributos privados es una cuestión de convención. Posiblemente no podría decirlo mejor que abarnert en su comentario:

Además, en lo que respecta al problema de contaminación del espacio de nombres con Ruby y JS, vale la pena señalar que este es un problema inherente a los lenguajes de tipo dinámico. En los lenguajes de tipo estático tan diversos como Haskell y C ++, las funciones gratuitas específicas de tipo no solo son posibles, sino también idiomáticas. (Ver el Principio de interfaz.) Pero en los lenguajes de tipado dinámico como Ruby, JS y Python, las funciones gratuitas deben ser universales. Una gran parte del diseño del lenguaje / biblioteca para lenguajes dinámicos es elegir el conjunto correcto de tales funciones.

Por ejemplo, acabo de dejar Ember.js a favor de Angular.js porque Estaba cansado de los conflictos de espacio de nombre en Ember; Angular maneja esto usando una elegante estrategia tipo Python de prefijación de métodos incorporados (con $thing en Angular, en lugar de subrayados como python), para que no entren en conflicto con los métodos y propiedades definidos por el usuario. Sí, el conjunto __thing__ no es particularmente bonito, pero me alegro de que Python haya adoptado este enfoque porque es muy explícito y evita PoLA clase de errores relacionados con los conflictos de espacios de nombres de objetos.


37