Comportamiento contrario a la intuición de int () en Python


83

Se indica claramente en los documentos que int (número) es una conversión de tipo de suelos:

int(1.23)
1

e int (cadena) devuelve un int si y solo si la cadena es un literal entero.

int('1.23')
ValueError

int('1')
1

¿Hay alguna razón especial para eso? Me parece contradictorio que la función se limite en un caso, pero no en el otro.

Respuestas:


123

No hay ninguna razón especial . Python simplemente está aplicando su principio general de no realizar conversiones implícitas, que son causas bien conocidas de problemas, particularmente para los recién llegados, en lenguajes como Perl y Javascript.

int(some_string)es una solicitud explícita para convertir una cadena a formato entero; las reglas para esta conversión especifican que la cadena debe contener una representación literal de entero válida. int(float)es una solicitud explícita para convertir un flotante en un número entero; las reglas para esta conversión especifican que la porción fraccionaria del flotante se truncará.

Para int("3.1459")poder regresar, 3el intérprete tendría que convertir implícitamente la cadena en un flotante. Dado que Python no admite conversiones implícitas, elige generar una excepción en su lugar.


type(3)devuelve <type int>. Sin embargo, Python no se queja float("3"). ¿Python no está convirtiendo implícitamente la cadena en un int y luego en un flotante?
franksands

No. "3" es un valor de punto flotante válido aunque, como literal de programa, se interpretaría como un número entero. No es necesaria la conversión de números enteros.
holdenweb

75

Es casi seguro que se trata de aplicar tres de los principios del Zen de Python :

Explícito es mejor implícito.

[...] practicidad vence pureza

Los errores nunca deben pasar en silencio

Un porcentaje de las veces, alguien int('1.23')está llamando a la conversión incorrecta para su caso de uso y quiere algo como floato en su decimal.Decimallugar. En estos casos, es claramente mejor para ellos obtener un error inmediato que puedan corregir, en lugar de dar silenciosamente el valor incorrecto.

En el caso de que usted no desea truncar que a un int, es trivial para hacer explícitamente pasándola a través de floatprimero, y luego llamando a uno de int, round, trunc, flooro ceilsegún el caso. Esto también hace que su código sea más autodocumentado, evitando que una modificación posterior "corrija" una intllamada hipotética truncada silenciosamente al floatdejar en claro que el valor redondeado es lo que desea.


Creo que esos principios se adoptaron mucho antes de que se formulara el Zen, pero de cualquier manera, los dos parecerían estar en armonía.
holdenweb

17

A veces, un experimento mental puede resultar útil.

  • Comportamiento A: int('1.23')falla con un error. Este es el comportamiento existente.
  • Comportamiento B: int('1.23')produce 1sin error. Esto es lo que propones.

Con el comportamiento A, es sencillo y trivial obtener el efecto del comportamiento B: utilícelo int(float('1.23'))en su lugar.

Por otro lado, con el comportamiento B, obtener el efecto del comportamiento A es significativamente más complicado:

def parse_pure_int(s):
    if "." in s:
        raise ValueError("invalid literal for integer with base 10: " + s)
    return int(s)

(e incluso con el código anterior, no tengo total confianza en que no hay un caso de esquina que se maneje mal).

Por tanto, el comportamiento A es más expresivo que el comportamiento B.

Otra cosa a considerar: '1.23'es una representación de cadena de un valor de punto flotante. Conversión '1.23'a un número entero conceptualmente implica dos conversiones (cadena a flotante a un entero), pero int(1.23)y int('1')cada implican sólo una conversión.


Editar:

Y de hecho, hay casos extremos que el código anterior no manejaría: 1e-2y 1E-2ambos son valores de punto flotante también.


Para aclarar: no propondría la conducta B, porque eso es peligroso, como usted y otros dijeron. No estoy seguro de que exista una solución mejor que la actual. Una opción sería dar nombres diferentes a las funciones, pero eso es solo más cosas para escribir. La solución obvia de que int (1.23) falle y solo int (float-with-no-decimal-places) devuelva un número entero no tiene sentido en un lenguaje escrito dinámicamente.
StefanS

1
El caso de la esquina podría ser int('123E-2')o int('1L').
Jared Goguen

11

En palabras simples, no son la misma función.

  • int (decimal) se comporta como 'piso, es decir, elimina la parte decimal y regresa como int'
  • int (cadena) se comporta como 'este texto describe un número entero, conviértalo y devuelve como int'.

Son 2 funciones diferentes con el mismo nombre que devuelven un número entero pero son funciones diferentes.

'int' es corto y fácil de recordar y su significado aplicado a cada tipo es intuitivo para la mayoría de los programadores, por eso lo eligieron.

No hay ninguna implicación de que estén proporcionando la misma funcionalidad o una combinación de funciones, simplemente tienen el mismo nombre y devuelven el mismo tipo. Podrían llamarse fácilmente 'floorDecimalAsInt' y 'convertStringToInt', pero eligieron 'int' porque es fácil de recordar, (99%) intuitivo y la confusión rara vez ocurre.

Analizar el texto como un número entero para el texto que incluye un punto decimal como "4.5" arrojaría un error en la mayoría de los lenguajes de computadora y se esperaría que arrojara un error para la mayoría de los programadores, ya que el valor de texto no representa un número entero e implica están proporcionando datos erróneos


2
Entonces, ¿por qué dos 'funciones diferentes' tienen el mismo nombre? Suena como una violación de una tontería zen.
hobbs

porque el nombre tiene sentido para 2 funciones diferentes y es sucinto. Int-ify un decimal (piso), convierta una cadena en un int (conversión)

Técnicamente, podría ayudar recordar que intes un tipo (y uno incorporado). Su creador ( __new__) toma varios tipos de argumentos posibles. Su comportamiento para cada tipo está bien definido.
holdenweb

Esta respuesta es simplemente incorrecta como se indica. intde hecho, no es una función sino un tipo, cuyos métodos __new__y __init__toman una cadena o un argumento flotante, tratando cada uno de ellos de manera apropiada. Sería más exacto decir que el tipo procesa los dos tipos de argumentos de manera diferente, pero solo hay uno int.
holdenweb
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.