2010-12-06 12 views
13

Python es más fuertemente tipado que otros lenguajes de scripting. Por ejemplo, en Perl:Python: ¿Hay alguna manera de evitar que ocurra una conversión automática de int a long int?

perl -E '$c=5; $d="6"; say $c+$d' #prints 11 

Pero en Python:

>>> c="6" 
>>> d=5 
>>> print c+d 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: cannot concatenate 'str' and 'int' objects 

Perl inspeccionará una cadena y convertir en un número, y el trabajo de los operadores + -/* ** como se espera con un número. PHP es similar.

Python usa + para concatenar cadenas por lo que el intento de operación de c+d falla porque c es una cadena, d un int. Python tiene un sentido más fuerte de numeric types que Perl. OK - Puedo lidiar con eso.

Pero tenga en cuenta:

>>> from sys import maxint 
>>> type(maxint) 
<type 'int'> 
>>> print maxint 
9223372036854775807 
>>> type(maxint+2) 
<type 'long'> 
>>> print maxint+2 
9223372036854775809 
>>> type((maxint+2)+maxint) 
<type 'long'> 
>>> print ((maxint+2)+maxint) 
18446744073709551616 

Ahora Python autopromote de un int, que en este caso es un poco largo, 64 (OS X, Python 2.6.1) a un long int Python que es de precisión arbitraria . Aunque los tipos no son iguales, son similares y Python permite el uso de los operadores numéricos habituales. Por lo general, esto es útil. Es útil para suavizar las diferencias entre 32 bits y 64 bits, por ejemplo.

La conversión de int a long es una manera:

>>> type((maxint+2)-2) 
<type 'long'> 

Una vez realizada la conversión, todas las operaciones en esa variable ahora se hacen en precisión arbitraria. Las operaciones de precisión arbitraria son órdenes de magnitud más lentas que las operaciones int nativas. En un script en el que estoy trabajando, me gustaría que la ejecución sea rápida y que se extienda a horas debido a esto. Considere lo siguiente:

>>> print maxint**maxint  # execution so long it is essentially a crash 

Así que mi pregunta: ¿Hay una manera de derrotar o no permitir la auto-promoción de un pitón int a una pitón long?

Editar, seguimiento: '¿por qué en la tierra que usted quiere tener un comportamiento desbordamiento estilo C'

recibí varios comentarios en la forma de El problema era que esta pieza de código funcionaba bien en 32 bits en C y Perl (con use int) con el comportamiento de desbordamiento de C. Hubo un intento fallido de transferir este código a Python. El comportamiento de desbordamiento diferente de Python resulta ser (parte) del problema. El código tiene muchos de esos diferentes modismos (C, Perl, algo de pitón) mezclados (y esos comentarios mezclados), por lo que fue un desafío.

Básicamente, el análisis de imágenes que se realiza es un filtro de paso alto basado en disco para realizar una comparación de imágenes similar. Parte del filtro de paso alto tiene una multiplicación basada en números enteros de dos polinomios grandes. El desbordamiento era esencialmente un tipo de lógica de "no me importa, es grande ...", por lo que el resultado fue el deseado con un desbordamiento basado en C. Entonces, el uso de la regla de Horner con O (n) fue un desperdicio ya que los polinomios más grandes serían simplemente "grandes", una forma de justicia aproximada de la aritmética de saturación de carot-top.

Cambiar la multiplicación de polinomios basados ​​en bucle a una forma de FFT es probablemente mucho más rápido.FFT se ejecuta casi en tiempo lineal frente a O (n) para la polinomial de regla de Horner multiplicar. Pasar de un disco a otro en la memoria también lo acelerará. Las imágenes no son terriblemente grandes, pero el código original se escribió en un momento en que se consideraban "enormes". El propietario del código no está listo para destruir su código querido, así que ya veremos. La 'respuesta correcta' para él probablemente sea mantener a Perl o C si él quiere ese código.

Gracias por las respuestas. No sabía acerca del módulo decimal de Python, y parecía ser lo más parecido a lo que estaba preguntando, ¡aunque hay otros problemas que resolver en este caso!

+1

maxint ** maxint es un número con >> 750 decimales, espero que no estés realmente sorprendido, lleva un tiempo. Además, ¿qué se supone que debe suceder cuando un número no cabe en 32 bits? –

+1

¿Estás diciendo que las operaciones matemáticas básicas hacen que tu aplicación funcione durante horas más de lo normal? Eso suena como su error, no python's – Falmarri

+1

Además, ¿qué debería pasar en lugar de autopromoción? Segfault? Parece que debe mantener sus números debajo de sys.maxint ... – Falmarri

Respuesta

4

Así que quiere tirar el One True Way y volverse retro en los desbordamientos. Tontito.

No hay una buena ventaja para el estilo de desbordamiento de C/C++/C#/Java. It does not reliably raise an error condition. Para C y C99, es un "comportamiento indefinido" en ANSI y POSIX (C++ obliga a devolver el módulo) y es un riesgo de seguridad conocido. ¿Por qué quieres esto?

El Python method de desbordamiento continuo a largo es la mejor manera. Creo que este es el mismo comportamiento que está adaptado por Perl 6.

Puede utilizar el Decimal module para obtener más desbordamientos finitos:

>>> from decimal import * 
>>> from sys import maxint 
>>> getcontext() 
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, 
flags=[], traps=[DivisionByZero, Overflow, InvalidOperation]) 

>>> d=Decimal(maxint) 
>>> d 
Decimal('9223372036854775807') 
>>> e=Decimal(maxint) 
>>> f=d**e 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 2225, in __pow__ 
    ans = ans._fix(context) 
    File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 1589, in _fix 
    return context._raise_error(Overflow, 'above Emax', self._sign) 
    File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 3680, in _raise_error 
    raise error(explanation) 
decimal.Overflow: above Emax 

Puede configurar su precisión y condiciones de contorno con clases decimal y el desbordamiento se casi inmediato. Puedes configurar lo que atrapas. Puede establecer su max y min. Realmente, ¿cómo se pone mejor que esto? (No sé la velocidad relativa para ser honesto, pero sospecho que es más rápido que aburrido pero más lento que los nativos, obviamente ...)

Para su problema específico de procesamiento de imagen, esto suena como una aplicación natural para considerar alguna forma de saturation arithmetic. También podría considerar, si tiene desbordamientos en 32 aritmética, verificar operandos en el camino en casos obvios: pow, **, *. Puede considerar overloaded operators y verificar las condiciones que no desea.

Si los operadores decimales, de saturación o sobrecargados no funcionan - you can write an extension. El cielo te ayudará si quieres deshacerte de la forma de desbordamiento de Python para volverse retro ...

1

No sé si sería más rápido, por supuesto, pero podría usar matrices numpy de un elemento en lugar de ints.

Si el cálculo específico que está preocupado por la exponenciación es entero, entonces hay algunas inferencias que podemos sacar:

def smart_pow(mantissa, exponent, limit=int(math.ceil(math.log(sys.maxint)/math.log(2)))): 
    if mantissa in (0, 1): 
     return mantissa 
    if exponent > limit: 
     if mantissa == -1: 
      return -1 if exponent&1 else 1 
     if mantissa > 1: 
      return sys.maxint 
     else: 
      return (-1-sys.maxint) if exponent&1 else sys.maxint 
    else: # this *might* overflow, but at least it won't take long 
     return mantissa ** exponent 
+1

será mucho más lento: la velocidad de numpy proviene del manejo total de muchos elementos del mismo tipo. La sobrecarga por artículo es bastante significativa, especialmente en comparación con una int. Además, Numpy no necesariamente te advertirá si desbordes tus enteros. –

+1

No conozco demasiadas formas de obtener de manera fiable o portátil información de desbordamiento * después del hecho * incluso en C. Si lo desea, es probable que necesite escribir un código que compruebe explícitamente los cálculos que se desbordarán, como 'if (MAX_INT - b) SingleNegationElimination

+0

Consulte [Seguridad de enteros] (http://ptgmedia.pearsoncmg.com/images/0321335724/samplechapter/seacord_ch05.pdf) para conocer numerosas formas de esto en C. – dawg

1

Bueno, si no se preocupan por la precisión que podría todas sus operaciones matemáticas modulo maxint.

+0

Evaluar '(maxint ** maxint)% maxint' va a ser igual de lento. –

+1

@Glenn Maynard: 'pow (maxint, maxint, maxint)' es mucho más rápido. – jfs

2

Int vs long es un legado histórico: en python 3, cada int es un "largo". Si la velocidad de su script está limitada por el cálculo interno, es probable que lo esté haciendo mal.

Para darle una respuesta correcta, necesitamos más información sobre lo que está tratando de hacer.

+0

Es difícil decir "exactamente" lo que estoy haciendo porque aproximadamente el 75% se corta y pega con código. Soy principalmente un tipo Perl y estoy aprendiendo Python sobre la marcha. Sé lo suficiente de python para ver POR QUÉ está ralentizando aleatoriamente; estas son firmas de imagen de 32 bits y 99.99% son con 2^32. El 0.01% es increíblemente lento, lo que he rastreado hasta un desbordamiento de 32 bits desde una firma de imagen. Mi primera inclinación fue (¡Sorpresa!) A reescribir el código ofensivo en C o Perl, pero pensé que iba a probar esta idea ... – dawg

+0

¿Puede darnos el código que calcula esa firma? –

+0

Son 4000 líneas y tienen derechos de autor para mi cliente ..... – dawg

5

Si desea que se desborden los desbordamientos aritméticos, p. 32 bits, puede usar, p. numpy.uint32.

Eso le da una advertencia cuando ocurre un desbordamiento.

>>> import numpy 
>>> numpy.uint32(2**32-3) + numpy.uint32(5) 
Warning: overflow encountered in ulong_scalars 
2 

He probado su velocidad sin embargo:

>\python26\python.exe -m timeit "2**16 + 2**2" 
1000000 loops, best of 3: 0.118 usec per loop 

>\python26\python.exe -m timeit "2**67 + 2**65" 
1000000 loops, best of 3: 0.234 usec per loop 

>\python26\python.exe -m timeit -s "import numpy; numpy.seterr('ignore')" "numpy.uint32(2)**numpy.uint32(67) + numpy.uint32(2)**numpy.uint32(65)" 
10000 loops, best of 3: 34.7 usec per loop 

No se ve bien para la velocidad.

+1

será mucho más lenta que la aritmética de enteros habitual, ya que la sobrecarga de numpy para el manejo por artículo es bastante significativa –

+3

Ha llamado al constructor cuatro veces en cada ciclo, lo que va a ser muy costoso. En su lugar, debe almacenar en caché los objetos 'uint32'. – nneonneo

2

Puede forzar sus valores para volver a la normal int s si incluye un num = int(num) ocasionalmente en el algoritmo. Si el valor es largo pero se ajusta en un int nativo, descenderá a int. Si el valor no cabe en un int nativo, seguirá siendo largo.