如何为 python 中的数字 class 加速 __init__?

How to speed up __init__ for a Number class in python?

作为(现已完成)挑战的一部分,我最终创建了一个分数 class 供使用。在对我的整体代码进行概要分析后,我在 __init__ 上花费了不到一半的时间 class 而不是做一些有用的事情。

这里是 __init__:

class fraction(Number):

    __slots__ = ("n", "d")
    def __init__(self, n, d, returnint = False) -> None:

        if type(n) == int and type(d) == int:
            pass
        elif type(n) not in (fraction, int) or type(d) not in (fraction, int):
            raise TypeError("Can't create fraction ({a}, {b}) numerator and denominator must be int or fraction.".format(a = n, b = d))
        else:
            if type(n) == fraction:
                d = d * n.d
                n = n.n
            if type(d) == fraction: 
                n = n * d.d
                d = d.n
        
        if d == 0:
            raise ValueError("Denominator cannot be zero")
        
        a = n
        b = d
        while not b == 0:
            r = a%b
            a = b
            b = r
        gcd = int(a)
        self.n = int(n/gcd)
        self.d = int(d/gcd)

下面是 cprofile 对整体代码的示例输出:

1971273 function calls (1935347 primitive calls) in 3.232 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
...
272503/250039    1.516    0.000    1.541    0.000 doomsday.py:46(__init__)
...

是的,我正在创建它们的 lot 对于我的解决方案的某些变体,我可能会调整算法以避免分数但是......它真的很麻烦,并且在其他情况下,如果没有很多分数,我就无法逃脱。

我还尝试拆分简化步骤(从 a=ngcd = int(a))并重新分析 - 这表明 50-60% 的时间都在进行此操作(但增加了 10额外函数调用的 % 开销)。我的 运行 时间还有不到 25% 用于简单地启动对象而不进行实际计算。

我怎样才能加快速度?

感谢评论者指点

tldr;使用 class 会有开销。重构主要代码以使用整数数学更快。

使用对象/class 有明显的开销。我通过两种方式改进了原始代码的性能:

  1. 使用 isinstance() 代替 type() ==
  2. 删除简化代码中对 int() 的调用

进行这些更改后,在 __init__ 中花费的时间大约有一半是开销(包括 if 语句)。另一半是运行的简化。

基于此,我还重构了我的主要代码,通过实施 Bareiss 算法(请参阅此处的(我的)答案:https://scicomp.stackexchange.com/questions/34512/bareiss-algorithm-vs-lu-decomposition/41262#41262)来使用整数数学来计算矩阵行列式,并避免使用分数可能。

最终代码如下所示:

class fraction(Number):

    __slots__ = ("n", "d")
    def __init__(self, n, d, returnint = False) -> None:

        if isinstance(n, int) and isinstance(d, int):
            pass
        elif not isinstance(n, (fraction, int)) or not isinstance (d, (fraction, int)):
            raise TypeError("Can't create fraction ({a}, {b}) numerator and denominator must be int or fraction.".format(a = n, b = d))
        else:
            if isinstance(n, fraction):
                d = d * n.d
                n = n.n
            if isinstance(d, fraction): 
                n = n * d.d
                d = d.n
        
        if d == 0:
            raise ValueError("Denominator cannot be zero")
        
        a = n
        b = d
        while not b == 0:
            r = a%b
            a = b
            b = r
        gcd = a
        self.n = n//gcd
        self.d = d//gcd

为了完整起见,这里有一个关于设置对象开销的视图:

from timeit import timeit


class dummy(object):
    __slots__ = "i"

    def __init__(self, i = 0):
        self.i = i

def do_int(x):
    a = x

def do_dummy(x):
    a = dummy(x)

print("Int")
print(timeit(lambda: do_int(6)))

print("Class")
print(timeit(lambda: do_dummy(6)))

给出:

Int
0.261943900026381
Class
0.7345267999917269