¿Por qué Python tiene una función de formato así como un método de formato?

La función de format en los elementos integrados parece ser como un subconjunto del método str.format utilizado específicamente para el caso de un formato de un solo objeto.

p.ej.

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

aparentemente se prefiere a

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

e IMO, se ve mejor, pero ¿por qué no usar str.format en todos los casos para simplificar las cosas? Ambos se introdujeron en 2.6 por lo que debe haber una buena razón para tener ambos a la vez, ¿cuál es?

Edit: preguntaba sobre el format y format str.format , no por qué no tenemos un (13).format

Creo que format y str.format hacen cosas diferentes. Aunque puede usar str.format para ambos, tiene sentido tener versiones separadas.

La función de format nivel superior es parte del nuevo “protocolo de formato” que admiten todos los objetos. Simplemente llama al método __format__ del objeto que se pasa y devuelve una cadena. Esta es una tarea de bajo nivel, y el estilo de Python es 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 el format y el format str.format .

El método str.format 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 varias otras transformaciones en los objetos. No solo piense en el "{}".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 formato de bajo nivel de cada elemento, str.format basa en la misma maquinaria del protocolo de formato, por lo que puede enfocar sus propios esfuerzos en el nivel superior. Dudo que en realidad llame al format incorporado, en lugar de a los métodos de __format__ de sus argumentos, pero eso es un detalle de la implementación.

Mientras que ("{"+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 el cadena de formato para comprobar si hay alguna de las cosas complicadas. Sin embargo, la sobrecarga puede perderse en el ruido en un progtwig real.

Cuando se trata de uso (incluidos los ejemplos en el Desbordamiento de stack), puede ver más uso de str.format simplemente porque algunos progtwigdores no conocen el format , que es nuevo y bastante oscuro. Por el contrario, es difícil evitar el str.format (A menos que haya decidido quedarse con el operador % para todo su formato). Por lo tanto, la facilidad (para usted y sus compañeros progtwigdores) de comprender una llamada de str.format puede superar cualquier consideración de rendimiento.

tldr; format simplemente llama a obj.__format__ y es usado por el método str.format que hace cosas aún más altas. Para el nivel inferior tiene sentido enseñar a un objeto cómo formatearse a sí mismo.

Es solo azúcar sintáctica.

El hecho de que esta función comparta 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: realiza una interpolación de cadena compleja (que reemplaza al operador % ); format puede formatear un solo objeto como cadena, el subconjunto más pequeño de la especificación str.format . Entonces, ¿por qué necesitamos format ?

La función de format es una alternativa a la obj.format('fmt') encuentra en algunos idiomas OO . Esta decisión es consistente con la razón de ser de len (por qué Python usa una función len(x) lugar de una propiedad x.length como Javascript o Ruby).

Cuando un lenguaje adopta la obj.format('fmt') (u obj.length , obj.toString y así sucesivamente), se evita que las clases tengan un atributo llamado format (o length , toString , se le ocurrió la idea), de lo contrario Sería una sombra del método estándar del lenguaje. En este caso, los diseñadores de lenguajes están poniendo la carga de prevenir choques de nombres en el progtwigdor.

Python es muy aficionado a PoLA y adoptó la __dunder__ (doble guión bajo) para los elementos incorporados con el fin de minimizar la posibilidad de conflictos entre los atributos definidos por el usuario y los elementos incorporados en el idioma. Así que obj.format('fmt') convierte en obj.__format__('fmt') , y por supuesto puede llamar a obj.__format__('fmt') lugar de format(obj, 'fmt') (de la misma manera que puede llamar obj.__len__() 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 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 enfoque tipográfico de pato de Python para OO y le da a los diseñadores de idiomas la libertad de cambiar / extender la implementación subyacente sin romper el código heredado.

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

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

Y aquí reposo mi caso.

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

Citando el mismo BDFL sobre len :

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

(a) Para algunas operaciones, la notación de prefijo solo se lee mejor que postfix – ¡las operaciones de prefijo (e infijo!) tienen una larga tradición en matemáticas que le gustan las notaciones donde los elementos visuales ayudan al matemático a pensar sobre un problema. Compara la facilidad con la que reescribimos una fórmula como x*(a+b) en x*a + x*b con la torpeza de hacer lo mismo con una notación OO sin formato.

(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 entero y el argumento es una especie de contenedor. Por el contrario, cuando leo x.len() , ya tengo que saber que x es un tipo de contenedor que implementa una interfaz o hereda de una clase que tiene un len() estándar len() . Observe la confusión que ocasionalmente tenemos cuando una clase que no implementa un mapeo tiene un método get() o keys() , o algo que no es un archivo tiene un método write() .

Al decir lo mismo de otra manera, veo ‘ len ‘ como una operación incorporada. Odiaría perder eso. /… /

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

Hay un razonamiento adicional sobre len en las preguntas frecuentes de diseño e historia . Aunque no es una respuesta tan completa o tan buena, es indiscutiblemente oficial. – abarnert

¿Es esta una preocupación práctica o simplemente la sincronización de syntax?

Esta es una preocupación muy práctica y del mundo real en lenguajes como Python, Ruby o Javascript porque en los lenguajes tipificados dinámicamente 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 ponerlo mejor que abarnert en su comentario:

Además, en lo que respecta al problema de la contaminación de los espacios de nombres con Ruby y JS, vale la pena señalar que se trata de un problema inherente a los lenguajes de tipo dynamic. En lenguajes de tipos estáticos tan diversos como Haskell y C ++, las funciones gratuitas específicas del tipo no solo son posibles, sino también idiomáticas. (Consulte El principio de interfaz ). Pero en lenguajes de tipo dynamic como Ruby, JS y Python, las funciones gratuitas deben ser universales. Una gran parte del diseño de bibliotecas / idiomas para lenguajes dynamics 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 nombres en Ember ; Angular maneja esto usando una elegante estrategia tipo Python de prefijo de métodos integrados (con $thing en Angular, en lugar de guiones bajos como python), por lo que no entran en conflicto con los métodos y propiedades definidos por el usuario. Sí, todo __thing__ no es particularmente bonito, pero me alegro de que Python haya __thing__ este enfoque porque es muy explícito y evita la clase de errores de PoLA con respecto a los choques de espacios de nombres de objetos.