【问题标题】:Python: Is there a way to keep an automatic conversion from int to long int from happening?Python:有没有办法防止从 int 到 long int 的自动转换发生?
【发布时间】:2010-12-06 00:55:18
【问题描述】:

Python 的类型比其他脚本语言更强。例如,在 Perl 中:

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

但在 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 将检查一个字符串并转换为一个数字,+ - / * ** 运算符可以按您对数字的期望工作。 PHP 类似。

Python 使用+ 连接字符串,因此c+d 的尝试操作失败,因为c 是字符串,d 是int。 Python 对numeric types 的感知比 Perl 更强。好的——我可以处理。

但是考虑一下:

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

现在 Python 将 autopromote 从一个 int(在本例中为 64 位长(OS X,python 2.6.1))转换为具有任意精度的 python long int。尽管类型不同,但它们是相似的,Python 允许使用通常的数字运算符。通常这很有帮助。例如,它有助于平滑 32 位和 64 位之间的差异。

intlong 的转换是一种方式:

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

转换完成后,对该变量的所有 操作现在都以任意精度完成。任意精度操作比原生 int 操作慢几个数量级。在我正在处理的一个脚本上,我会让一些执行变得很快,而其他的会因此延长到几个小时。考虑:

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

所以我的问题是:有没有办法让 Python int 自动升级为 Python long

编辑,跟进:

我收到了几个 cmet,形式是“你到底为什么想要 C 风格的溢出行为?”问题是这段特殊的代码在 C 和 Perl(使用use int)的 32 位上运行良好,具有 C 的溢出行为。将此代码移植到 Python 的尝试失败。 Python 的不同溢出行为原来是问题的(部分)。该代码混合了许多不同的习语(C、Perl、一些 python)(以及那些 cmets),因此具有挑战性。

本质上,正在进行的图像分析是一个基于圆盘的高通滤波器,用于执行相似的图像比较。高通滤波器的一部分具有两个大多项式的基于整数的乘法。溢出本质上是一种“不在乎,它很大......”的逻辑,因此结果与基于 C 的溢出一样。因此,在 O(n2) 时间中使用霍纳规则是一种浪费,因为较大的多项式只是“大”——carot-top 饱和算法的粗略正义形式。

将基于循环的多项式乘法更改为 FFT 的形式可能要快得多。对于霍纳规则多项式乘法,FFT 的运行时间接近线性,而 O(n2)。从基于磁盘到内存中也将加快这一速度。图像不是特别大,但原始代码是在它们被认为是“巨大的!!!”的时候编写的代码所有者还没有准备好丢弃他心爱的代码,所以我们拭目以待。对他来说,“正确答案”可能只是保留 Perl 或 C,如果他想要那个代码的话。

感谢您的回答。我不知道 Python 的十进制模块,这似乎最接近我的要求——尽管在这种情况下还有其他问题需要解决!

【问题讨论】:

  • maxint**maxint 是一个小数点>>750的数字,我希望你不会对它需要一段时间感到惊讶。还有当数字不适合 32 位时应该发生什么?
  • 您是说基本的数学运算使您的应用程序运行时间更长,否则会?这听起来像你的错误,而不是 python 的
  • 另外,应该怎么做而不是自动推广?段错误?听起来您应该将数字保持在 sys.maxint 以下...
  • 引发 OverflowError 是转换为 long 的完全合理的替代行为,尽管语言中没有这样做的机制(例如,仅针对您的代码而不是库可能没有理智,这意味着它会破坏事物)。
  • @Glenn Maynard:这确实是我一直在寻找的答案。有没有我作为新手看不到的“pythonic”方式或谷歌改变int的默认行为。 Numpy 可能是最接近的,但需要更改大量代码才能到达那里。

标签: python performance integer long-integer


【解决方案1】:

因此,您想抛弃 One True Way 并在溢出时复古。你傻。

C/C++/C#/Java 风格的溢出没有好的好处。 It does not reliably raise an error condition。对于 C 和 C99,它是 ANSI 和 POSIX 中的“未定义行为”(C++ 要求模返回),它是一个已知的安全风险。你为什么要这个?

无缝溢出到long的Python method是更好的方法。我相信这与 Perl 6 所采用的行为相同。

您可以使用Decimal module 获得更多有限溢出:

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

您可以使用 Decimal 类设置精度和边界条件,并且几乎立即溢出。你可以设置你的陷阱。您可以设置最大值和最小值。真的 - 它如何变得比这更好? (老实说,我不知道相对速度,但我怀疑它比 numby 快,但显然比原生 int 慢......)

对于您的特定图像处理问题,这听起来像是考虑某种形式的saturation arithmetic 的自然应用程序。您还可以考虑,如果您在 32 位算术上出现溢出,请在明显的情况下检查操作数:pow、**、*。您可以考虑overloaded operators 并检查您不想要的条件。

如果小数、饱和或重载运算符不起作用 -- you can write an extension。如果你想抛弃 Python 的溢出方式去复古,天堂会帮助你......

【讨论】:

    【解决方案2】:

    如果您希望算术溢出在例如32 位,您可以使用例如numpy.uint32.

    这会在发生溢出时向您发出警告。

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

    我测试了它的速度:

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

    速度看起来不太好。

    【讨论】:

    • 它将比通常的整数运算慢得多,因为 numpy 处理每个/项目的开销非常大
    • 你在每个循环中调用了构造函数四次,这将是非常昂贵的。相反,您应该缓存 uint32 对象本身。
    【解决方案3】:

    Int vs long 是一个历史遗留问题——在 python 3 中,每个 int 都是一个“long”。如果你的脚本速度受到 int 计算的限制,很可能你做错了。

    为了给你一个正确的答案,我们需要更多关于你想要做什么的信息。

    【讨论】:

    • 很难说“确切”我在做什么,因为大约 75% 是代码剪切和粘贴。我主要是一个 Perl 人,边走边学 Python。我确实知道足够多的 python 来了解它为什么会随机减速;这些是 32 位图像签名,99.99% 是 2^32。 0.01% 是令人难以置信的慢,我已经追踪到图像签名的 32 位溢出。我的第一个想法是(惊喜!!!)用 C 或 Perl 重写有问题的代码,但我想我会尝试一下这个想法......
    • @drewk:我相信 Python 使用 C signed int 实现其 int,这意味着在您的 Python 代码中(至少在 32 位平台上),任何 2**31 或更高版本都变为一个 Python long。这能解释你所看到的吗?
    • 你是如何追踪到 32 位溢出的?将 int 升级到 long 应该花费难以察觉的时间。
    • @Craig McQueen:我明白为什么我会看到这个。这是 32 位 Python 溢出的结果。它不会发生在我的 64 位 python 中。我可以解决这个特殊问题:以 64 位和 fugetaboutit 运行此脚本。问题更多的是好奇心和未来的知识。在 C 和 Perl 中,我会知道如何检测问题。在 Python 中,我不知道如何检测、控制或更改它。我想我可以用 numpy 做到这一点,但这似乎不太理想。
    • @Falmarri:More is good... all is better. 我认为这不是非此即彼的问题。我只想要一个立即发生的故障模式,而不是一个永远不会发生的故障模式。在大溢出的情况下,这两种模式都是失败的。能够设置或选择所需的行为会很好。 Decimal 模块有它(但它有点慢......`)默认情况下,Pythons 对 ints / longs 的行为很好且合乎逻辑。作为唯一可能的方式——我更喜欢选择。
    【解决方案4】:

    如果您偶尔在算法中包含num = int(num),您可以强制您的值恢复到正常的ints。如果该值很长但适合本机 int,它将降级为 int。如果该值不适合原生 int,它将保持为 long。

    【讨论】:

      【解决方案5】:

      好吧,如果您不关心准确性,您可以将所有数学运算都取模 maxint。

      【讨论】:

      • 评估(maxint**maxint) % maxint 仍然会很慢。
      • @Glenn Maynard:pow(maxint, maxint, maxint) 更快。
      【解决方案6】:

      我不知道它是否会更快,但你可以使用一个元素的 numpy 数组而不是整数。

      如果你关心的具体计算是整数取幂,那么我们可以得出一些推论:

      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
      

      【讨论】:

      • 它会慢得多 - numpy 的速度来自于处理大量相同类型的项目。每个项目的开销非常大,尤其是与一个 int 相比。此外,如果您的整数溢出,numpy 不一定会警告您。
      • 我不知道有太多方法可以可靠或可移植地事后获取溢出信息,即使在 C 中也是如此。如果你想要这个,你可能需要编写代码显式检查将溢出的计算,例如if (MAX_INT - b) &lt; a,或在汇编语言中访问溢出状态标志。
      • 请参阅Integer Security 了解 C 中的多种方法。
      • 我一定遗漏了一些东西,我没有在该论文中看到任何方法来说明如何在整数溢出发生后可移植地检测它们。有很多方法(类似于我提供的示例)它展示了如何检测是否会发生溢出,它还展示了一些 x86 程序集,这些程序集提供了对检测错误的硬件条件寄存器的访问。感谢您为我的回答提供支持。
      • @@TokenMacGuy:我想你以为我不同意你的观点。我同意发布的链接。重读我的评论,我想这不是太清楚! ;-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-28
      • 2016-07-09
      • 1970-01-01
      相关资源
      最近更新 更多