2010-12-06 6 views
13

Python ist stärker typisiert als andere Skriptsprachen. Zum Beispiel in Perl:Python: Gibt es eine Möglichkeit, eine automatische Konvertierung von int zu long int zu verhindern?

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

Aber in 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 wird eine Zeichenfolge und konvertiert in eine Zahl, und die + -/* ** Betreiber Arbeit überprüfen, wie Sie mit einer Reihe erwarten. PHP ist ähnlich.

Python verwendet + Strings verketten, so dass die die versuchte Operation von c+d schlägt fehl, weil c ist eine Zeichenfolge, d ein int. Python hat einen stärkeren Sinn für numeric types als Perl. OK - damit kann ich umgehen.

Aber bedenken Sie:

>>> 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 

Jetzt Python wird autopromote von einem int, die in diesem Fall ist ein 64-Bit lang (O X, Python 2.6.1) zu einem Python long int, die von beliebiger Genauigkeit ist . Auch wenn die Typen nicht gleich sind, sind sie ähnlich und Python erlaubt die Verwendung der üblichen numerischen Operatoren. Normalerweise ist das hilfreich. Es ist hilfreich, zum Beispiel die Unterschiede zwischen 32 Bit und 64 Bit zu glätten.

Die Umwandlung von int zu long ist ein Weg:

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

Sobald die Konvertierung vorgenommen wird, alle Operationen an diesen Variablen werden nun in beliebiger Genauigkeit erfolgen. Die Operationen mit beliebiger Genauigkeit sind um Größenordnungen langsamer als die nativen Int-Operationen. In einem Skript, an dem ich gerade arbeite, würde ich eine gewisse Ausführung haben, die bissig ist und andere, die sich dadurch in Stunden ausdehnen. Bedenken Sie:

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

Also meine Frage: Gibt es eine Möglichkeit erlauben zu besiegen oder die Auto-Förderung eines Python int zu einem Python long?

bearbeiten, Follow-up: ‚warum auf der Erde würden Sie wollen, C-Stil Überlaufverhalten haben‘

ich einige Kommentare in Form erhalten Das Problem war, dass dieser bestimmte Codecode auf 32 Bits in C und Perl (mit use int) mit dem Überlaufverhalten von C einwandfrei funktionierte. Es gab einen fehlgeschlagenen Versuch, diesen Code nach Python zu portieren. Pythons unterschiedliches Überlaufverhalten erweist sich als (Teil) des Problems. Der Code hat viele dieser verschiedenen Idiome (C, Perl, etwas Python) gemischt (und diese Kommentare gemischt), also war es eine Herausforderung.

Im Wesentlichen ist die Bildanalyse, die durchgeführt wird, ein scheibenbasierter Hochpassfilter, um einen ähnlichen Bildvergleich durchzuführen. Ein Teil des Hochpassfilters hat eine ganzzahlige Multiplikation von zwei großen Polynomen. Der Überlauf war im Wesentlichen eine "nicht - Vorsicht, es ist groß ..." Art von Logik, so dass das Ergebnis mit einem C-basierten Überlauf beabsichtigt war. Die Verwendung der Horner-Regel mit O (n) Zeit war daher eine Verschwendung, da die größeren Polynome einfach "groß" wären - eine grobe Gerechtigkeitsform der Sättigungsarithmetik des Karussells.

Die Änderung der Schleifenpolynom-Multiplikation in eine Form der FFT ist wahrscheinlich wesentlich schneller.FFT läuft in der Nähe der linearen Zeit gegen O (n) für Horner-Regelpolynom multiplizieren. Der Wechsel von Disc-basiert zu In-Memory wird dies ebenfalls beschleunigen. Die Bilder sind nicht schrecklich groß, aber der ursprüngliche Code wurde zu einer Zeit geschrieben, als sie als "riesig !!!" Der Code-Besitzer ist nicht bereit, seinen geliebten Code zu zerstören, also werden wir sehen. Die "richtige Antwort" für ihn ist wahrscheinlich nur Perl oder C, wenn er diesen Code möchte.

Danke für die Antworten. Ich wusste nichts über Pythons Dezimalmodul, und das schien am nächsten zu dem zu sein, wonach ich fragte - obwohl es in diesem Fall noch andere Probleme zu lösen gibt!

+1

maxint ** maxint ist eine Zahl mit >> 750 Dezimalstellen, ich hoffe du bist nicht wirklich überrascht, es dauert eine Weile. Auch was soll passieren, wenn eine Nummer nicht in 32 Bit passt? –

+1

Sie sagen, dass grundlegende mathematische Operationen Ihre Anwendung Stunden länger laufen lassen, sonst würde es? Das klingt wie dein Fehler, nicht Pythons – Falmarri

+1

Auch was sollte statt Autopromotion passieren? Segfault? Klingt wie du solltest deine Zahlen unter sys.maxint halten ... – Falmarri

Antwort

4

Sie wollen also den One True Way wegwerfen und retro auf Überläufe gehen. Dumm du.

Es gibt keinen guten Kopf für den Überlauf von C/C++/C#/Java. It does not reliably raise an error condition. Für C und C99 ist es "undefiniertes Verhalten" in ANSI und POSIX (C++ verlangt Modulo-Return) und es ist ein bekanntes Sicherheitsrisiko. Warum willst du das?

Die Python method von nahtlos überläuft zu einem langen ist der bessere Weg. Ich glaube, dies ist das gleiche Verhalten von Perl angepasst ist, 6.

Sie die Decimal module können mehr endliche Überläufe zu erhalten:

>>> 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 

Sie Ihre Präzision und Randbedingungen mit Dezimal-Klassen einstellen können und der Überlauf ist fast sofort. Sie können festlegen, wofür Sie eine Falle stellen. Sie können Ihre max und min einstellen. Wirklich - Wie wird es besser als das? (Ich weiß nichts über die relative Geschwindigkeit, um ehrlich zu sein, aber ich vermute, dass es schneller ist als numby, aber langsamer als native ints offensichtlich ...)

Für Ihr spezifisches Problem der Bildverarbeitung klingt dies wie eine natürliche Anwendung, um eine Form von saturation arithmetic zu betrachten. Sie könnten auch in Betracht ziehen, wenn Sie Überläufe bei 32-Arithmetik haben, überprüfen Sie die Operanden auf dem Weg in offensichtlichen Fällen: pow, **, *. Sie könnten overloaded operators betrachten und nach den Bedingungen suchen, die Sie nicht wollen.

Wenn Dezimal, Sättigung oder überladene Operatoren nicht funktionieren - . Der Himmel hilft dir, wenn du den Python-Weg des Überlaufs rausschmeißen willst, um retro zu gehen ...

1

Ich weiß nicht, ob es schneller wäre, neccesarly, aber Sie könnten numpy Arrays von einem Element anstelle von Ints verwenden.

Wenn die spezifische Berechnung Sie sind besorgt über integer Potenzierung ist, dann gibt es einige Schlüsse wir ziehen:

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

es wird viel viel langsamer - numpy Geschwindigkeit kommt von der Handhabung einer Menge von Gegenständen des gleichen Typs insgesamt. Der Overhead pro Element ist ziemlich signifikant, insbesondere im Vergleich zu einem Int. Außerdem wird numpy Sie nicht unbedingt warnen, wenn Sie Ihre Ganzzahlen überlaufen. –

+1

Ich weiß nicht von zu vielen Möglichkeiten, um zuverlässig oder portabel Überlauf Informationen zu bekommen * nach der Tatsache * sogar in C. Wenn Sie dies wollen, müssen Sie wahrscheinlich schreiben Code, der explizit überprüft für Berechnungen, die Überlauf wie 'if (MAX_INT - b) SingleNegationElimination

+0

Siehe bitte [Integer Security] (http://ptgmedia.pearsoncmg.com/images/0321335724/samplechapter/seacord_ch05.pdf) für zahlreiche Möglichkeiten dazu in C. – dawg

1

Nun, wenn Sie sich nicht um Genauigkeit kümmern könnten Sie alle Ihre mathematischen ops Modulo maxint.

+0

Auswerten von '(maxint ** maxint)% maxint' wird immer noch genauso langsam sein. –

+1

@Glenn Maynard: 'pow (maxint, maxint, maxint)' ist viel schneller. – jfs

2

Int vs long ist ein historisches Vermächtnis - in Python 3 ist jedes int ein "long". Wenn Ihre Skriptgeschwindigkeit durch Int-Berechnungen begrenzt ist, ist es wahrscheinlich, dass Sie es falsch machen.

Um Ihnen eine richtige Antwort zu geben, benötigen wir weitere Informationen darüber, was Sie zu tun versuchen.

+0

Es ist schwer zu sagen "genau", was ich tue, weil etwa 75% Code cut and paste ist. Ich bin meistens ein Perl-Typ und lerne Python, während ich gehe. Ich kenne genug Python, um zu sehen, WARUM es sich zufällig verlangsamt; Dies sind 32-Bit-Bildsignaturen und 99,99% sind mit 2^32. 0,01% sind unglaublich langsam, was ich auf einen 32 Bit Überlauf von einer Bildsignatur zurückgeführt habe. Meine erste Neigung war (Überraschung !!!), den fehlerhaften Code in C oder Perl neu zu schreiben, aber ich dachte, ich würde dieser Vorstellung einen Versuch geben .... – dawg

+0

Können Sie uns den Code geben, der diese Unterschrift berechnet? –

+0

Es ist 4000 Zeilen und urheberrechtlich geschützt zu meinem Kunden ... – dawg

5

Wenn arithmetische Überläufe z. 32 Bits, könnten Sie z.B. numpy.uint32.

Dies gibt Ihnen eine Warnung, wenn ein Überlauf auftritt.

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

testete ich seine Geschwindigkeit aber:

>\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 

Es ist nicht für die Geschwindigkeit gut aussehend.

+1

wird es viel langsamer als normale Integer-Arithmetik sein, da numpys Overhead für pro/item Handling ziemlich signifikant ist –

+3

Sie haben den Konstruktor vier Mal in jeder Schleife aufgerufen, was sehr teuer werden wird. Stattdessen sollten Sie die 'uint32' Objekte selbst zwischenspeichern. – nneonneo

2

Sie können Ihre Werte zwingen, zu normalen int s zurückzukehren, wenn Sie gelegentlich einen num = int(num) in Ihren Algorithmus einschließen. Wenn der Wert lang ist, aber in einen nativen Int passt, wird er auf int reduziert. Wenn der Wert nicht in einen nativen Int passt, bleibt er lang.

Verwandte Themen