Python中的更快的fractions模块
有没有一个比fractions
模块更快的替代品,比如说一个cFractions
模块?就像cDecimal
模块是Decimal
模块的更快版本一样。因为fractions
模块运行得太慢了。
4 个回答
很遗憾,目前没有不需要编译外部依赖的C语言等价方案。根据你的需求,我做了一个简单的代码示例:https://gist.github.com/mscuthbert/f22942537ebbba2c31d4,可能会对你有帮助。
这个示例提供了一个函数 opFrac(num)
,它可以选择性地把整数、浮点数或分数转换成浮点数或分数,并且可以设置分母的上限(我用的是65535,因为我处理的是小分数);如果这个浮点数可以在二进制中精确表示(也就是说,它是某个2的幂的倍数),那么就不做任何改变。否则就把它转换成分数。同样地,如果分数可以在二进制中精确表示,我们就把它转换成浮点数;否则就不做改变。
这里的 Fraction(float).limit_denominator(x)
调用被提取成一个辅助函数 _preFracLimitDenominator
,这个函数只会创建一个 Fraction
对象,而不是通常情况下会创建的三个对象。
这个示例的使用场景不多,但在有需要的地方,效果非常好。比如在我的项目 music21 中,我们主要处理的音符通常是在一个节拍(整数)上,或者在半拍、四分之一拍、八分之一拍等(这些在二进制中可以精确表示),但在一些比较少见的情况下,当音符的放置(偏移)或持续时间是,比如说,三分之一拍或五分之一拍时,我们就会遇到浮点数转换的问题,这导致了一些难以发现的bug。我们的测试用例在使用浮点数偏移和持续时间时需要350秒,而把所有的转换成分数后,时间增加到了1100秒——这完全不可接受。后来我们改用可选的分数和快速创建分数的方法,时间又回到了360秒,只损失了3%的性能。
如果你能接受有时候使用浮点数,有时候使用分数,这可能是一个不错的选择。
我也遇到了缺少这个包的问题,于是决定自己实现一个叫做 cfractions
的包(源代码可以在 Github 上找到)。
我们只需要安装它
/path/to/python3 -m pip install cfractions
然后在你的模块中把 fractions
替换成 cfractions
,就这么简单。
主要特点包括:
占用更少的内存
>>> from cfractions import Fraction >>> import sys >>> sys.getsizeof(Fraction()) 32
相比于
>>> from fractions import Fraction >>> import sys >>> sys.getsizeof(Fraction()) 48
所以它基本上是一个普通的 Python
object
加上2
个指针,用来表示分子和分母。速度更快:
- 可以从一对
int
创建 - 可以从一个
float
创建 - 可以从
str
创建 - 可以对
n
个实例求和 - 可以对
n
个实例求积或者如果我们看看相对性能
我们可以看到
fractions.Fraction
的表现非常好,太棒了!
注意:我使用了
perfplot
包,所有基准测试都在Python3.9.4
上运行。- 可以从一对
支持
Python3.5+
,使用普通的 Python C API,没有额外的依赖,
可以从分子/分母对、单个
int
/float
/任何numbers.Rational
值、str
(从 版本1.4.0
开始)构建,支持所有算术和比较操作,
字符串表示(包括
__repr__
和__str__
),支持
pickle
和copy
,不可变性和可哈希性,
可以与
int
和float
进行操作(对于后者,将Fraction
实例转换为float
,就像fractions.Fraction
一样),支持
PyPy
(通过回退到fractions.Fraction
代理),使用
Hypothesis
框架 进行所有操作的基于属性的测试。
它不包括的内容:
- 不支持与
complex
进行操作。