¡Simule el tipo de datos subyacente de Python, la realización de números enteros grandes!

Nos referimos a la lógica del código fuente de C e intentamos usar Python para simular la realización de enteros grandes, aquí solo se implementa la operación de suma y resta.

(Gracias al autor por su columna, he ganado mucho, consulte Mukenet: "Análisis en profundidad del código fuente de Python")

(1) Clase: Int32 ()

  • Está diseñado para imitar los datos enteros de 4 bytes del tipo int .
  • Define 4 operaciones básicas de suma, resta, multiplicación y división.
  • Utilice el descriptor de datos para limitar los datos. El valor más grande es solo -2 ** 31 a 2 ** 31 -1
  • Cuando el resultado de la operación es mayor que este rango, lanzará una excepción, lo que le indicará un desbordamiento de enteros
  • Una función de conversión, se calcula un valor grande cuando el resultado se convierte en un tipo entero grande (PyLongObject ())
  • Cabe señalar que no hay ninguna operación entre Int32 y PyLongObject. Por lo tanto, la operación no se realiza en Int32, y se convierte directamente internamente y se devuelve a PyLongObject, con el propósito de confusión de tipos. El propio usuario llama al método para convertir int32 en un entero grande para el cálculo.

(2) Clase: PyLongObject ()

  • Define números enteros grandes y define operaciones de suma y suma de números enteros grandes
  • Defina el método de clase: conver , que puede ser llamado directamente por la clase. Cuando el valor convertido de int32 es pequeño, puede directamente a = PyLongObject (). Conver (1231232132132131231232131231231231) para crear un objeto entero supergrande. Además, la función de conversión convert () de Int32 también se implementa llamando a este método.
  • También se define el método conver2 y se agrega el decorador @property . Cuando el valor de un entero grande es grande, el método se puede llamar directamente como una propiedad para devolver el valor del tipo python y observar su valor.

(3) 类 : PyLong_Type ()

  • Se definen la suma y resta de valor absoluto, que se llaman cuando la instancia del objeto PyLongObject () realiza operaciones .
  • Además, el objeto de tipo proporciona la función de método correspondiente, que se define directamente como singleton , y el objeto de instancia es globalmente único, lo que ahorra gastos de memoria.

(4) Base = 2 ** 30 Una constante global definida en la parte superior. Cuando una matriz representa un entero grande, representa el almacenamiento más grande de cada unidad, que también es equivalente a un sistema hexadecimal. Al realizar operaciones básicas de suma y resta, no excederá el tamaño representado por int32, lo cual es conveniente para el cálculo y también es un detalle.

El código anterior a continuación: (La parte de comentario es el código de función del programa de prueba, que no tiene ningún otro efecto)

# 这里用来实现,大整数
Base = 2 ** 30

# 定义 大整数的加减操作
class PyLong_Type():
    '''
        tp_as_number 指向 PyNumberMethods 方法集
        把类型对象设置为单例,全局只有一份
    '''
    def __new__(cls, *args, **kwargs):
        if not hasattr(PyLong_Type, "_instance"):
            cls._instance = super().__new__(cls, *args, **kwargs)

        return cls._instance
        
    # 直接将方法集中的方法定义到 PyLong_Type 里了
    def x_add(self, a, b):
        ''' 绝对值加法 '''
        size_a = abs(a.ob_size)
        size_b = abs(b.ob_size)
        carry = 0

        if size_a < size_b:
            a, b = b, a
            size_a ,size_b = size_b, size_a
        z = PyLongObject(ob_size=size_a + 1)
        
        # 解决 有 0 参与加法的情况(C语言的下面两个循环用python来写,无法解决有0的参与运算。因为python最后的  i 不会自增 1,C语言写的,加入第一个数字是零,那么直接从第二个循环开始,但是python从第二个循环开始,j 值 是等于 i+ 1的,也就是 C 与python的区别, 第一个i++会根据情况加, python的range只能使 i循环到比循环数小 1)
        if size_a == 0:
            return b
        elif size_b == 0:
            return a
        else:
            pass
        
        for i in range(size_b):
            carry += a.ob_digit[i] + b.ob_digit[i]
            z.ob_digit[i] += carry % Base
            # 得到进位
            carry = carry // Base


        for j in range(i+1, size_a):
            carry += a.ob_digit[j]
            z.ob_digit[j] = carry % Base
            carry = carry // Base
        
        # carry 可能还有一个进位
        if carry != 0:
            z.ob_digit[size_a] = carry
        else:
            del z.ob_digit[size_a] 
            z.ob_size -= 1
        
        return z
    
    def x_sub(self, a, b):
        ''' 绝对值减法运算 '''
        size_a = abs(a.ob_size)
        size_b = abs(b.ob_size)

        z = PyLongObject()
        # 绝对值减法结果符号, 1表示正, -1表示负
        sign = 1
        # 表示 向高位的借位
        borrow = 0
        # 用来循环计数(没有定义这个,有0参与运算时下面逻辑,可能会出错)
        i = 0

        # 算法类似于 上面的绝对值加法, 确保a 的绝对值长度 大于等于b
        if size_a < size_b:
            sign = -1 
            a, b = b, a
            size_a, size_b = size_b, size_a
        # 当两者相同长度的时候,挨个从高位依次比较,将大的数,赋值给a
        elif size_a == size_b:
            for i in range(size_a - 1, -1, -1):
                if a.ob_digit[i] != b.ob_digit[i]:
                    break
            
            if (i-1 < 0):
                return z
            if (a.ob_digit[i] < b.ob_digit[i]):
                sign = -1
                a, b = b, a
            # 此时 大于i的数相等,相减为0,只需从 i位置往后进行计算即可
            size_a = size_b = i + 1
            i = 0

        z = PyLongObject(size_a)
        for i in range(size_b):
            borrow = a.ob_digit[i] - b.ob_digit[i] - borrow
            z.ob_digit[i] = borrow % Base
            # 三元表达式
            borrow = 1 if borrow < 0 else 0
        
        if i == 0:
            i = -1
        # (a 长度大于 b的情况 )当还有借位时,继续
        for j in range(i + 1, size_a):
            borrow = a.ob_digit[j] - borrow
            z.ob_digit[j] = borrow % Base
            borrow = 1 if borrow < 0 else 0

        # 判断结果 正负
        if sign < 0:
            z.ob_size = -z.ob_size
        return z

# 自定义大整数类型:
class PyLongObject():
    Py_long = PyLong_Type()
    
    def __init__(self, ob_size = 0):
        """初始化大整数

        :param    ob_refcnt: 引用计数
        :param    ob_type: 类型指针( 这里规定它所使用的方法如 + - 操作 )
        :param    ob_size:  ob_digit 数组的长度
        :param    ob_digit: 用来保存大整数的绝对值(将大整数分为若干段进行保存)
        """ 

        # PyVarobject(变长对象)
        self.ob_refcnt = 1 
        self.ob_type = PyLong_Type
        self.ob_size = ob_size
        self.ob_digit = []
        
        for i in range(self.ob_size):
            self.ob_digit.append(0)


    # 重写加减法运算
    def __add__(self, other):
        # 两个数字均不大于 2*30, 那么直接转换为 原类型(Int32), 相加的数字不会超过2 **32 
        if abs(self.ob_size) <= 1 and abs(other.ob_size) <= 1:
            return PyLongObject.conver(self.conver2 + other.conver2) 

        if self.ob_size < 0:
            if other.ob_size < 0:
                z = self.Py_long.x_add(self, other)
                z.ob_size = -z.ob_size
            else:
                z = self.Py_long.x_sub(other, self)
        else:
            if other.ob_size < 0:
                z = self.Py_long.x_sub(self, other)
            else:
                z = self.Py_long.x_add(self, other)
        return z

    def __sub__(self, other):
        ''' 与加法得算法逻辑基本相同,都是调用 绝对值加减法来实现 '''
        if abs(self.ob_size) <= 1 and abs(other.ob_size) <= 1:
            return PyLongObject.conver(self.conver2 - other.conver2)
        
        if self.ob_size < 0:
            if other.ob_size < 0:
                z = self.Py_long.x_sub(self, other)
            else:
                z = self.Py_long.x_add(self, other)
            z.ob_size = -z.ob_size
        else:
            if other.ob_size < 0:
                z = self.Py_long.x_add(self, other)
            else:
                z = self.Py_long.x_sub(self, other)

        return z

    def __mul__(self, scale):
        raise("暂无乘法功能,敬请期待~")

    def __truediv__(self, scale):
        raise("暂无除法功能,敬请期待~")


    @classmethod
    def conver(self, num):
        ''' 实现进制的转换 '''
        a = PyLongObject()

        
        if isinstance(num, Int32):
            m = num.num
            z = abs(num.num)
            print(m, '---',z)
        else:
            if num < 0:
                m = num
                z = abs(num)
            else:
                m = z = num        

        if z == 0:
            return a
        else:
            while(z != 0):
                a.ob_digit.append(z % Base)
                print(z % Base)
                a.ob_size += 1
                z //= Base            
            if m < 0:
                a.ob_size *= -1
            return a
    
    @property
    def conver2(self):
        '''转换回 Int32 数字'''
        dec = 0
        for i, num in enumerate(self.ob_digit):
            dec += num * Base ** i     
        # 三元表达式,为值增加负号           
        dec = dec * -1 if self.ob_size < 0  else dec  
        return dec

# 因为是用Python来模拟大整数,但是 Python的数字计算已经是大整数了,所以我们来模拟一下,自建对象将Python的整形缩小到32位int


# 定义数据描述器,限制整数的大小为32位int型
class NumField():

    def __init__(self, attrname):
        self.attrname = attrname

    def __get__(self, instance, owner):
        return getattr(instance, self.attrname)

    def __set__(self, instance, value):
        if -2**31 <= value and  value <= 2**31 -1:
            setattr(instance, self.attrname, value)
        else:
            raise("整数溢出,超出最大长度")


# 自定义整数,大小不能超过int(32字节)
class Int32(): 
    num = NumField("_num")
    pylong = PyLongObject

    def __init__(self, num):
        self.num = num
        # self.num_long = self.__convert(num)

    def __str__(self):
        return str(self.num)

    # 重写加减法运算
    def __add__(self, other):
        return Int32(self.num + other.num)

    def __sub__(self, other):
        return Int32(self.num - other.num)

    def __mul__(self, other):
        return Int32(self.num * other.num)

    def __truediv__(self, other):
        return Int32(self.num / other.num)

    def convert(self):     
        a = self
        return self.pylong.conver(a)


# 测试代码逻辑正确性
# 测试 1(测试绝对值加法): 大整数表示的 数字连加 9999次 和 实际python数字运算 9999次 值转化一致,是否相等
'''
suab = Int32(1073741823)

b = 

g = b.convert()

c = PyLongObject()

for i in range(9999):
    print("第",i,"次")
    c += g
    print(g.conver2, g.ob_size, g.ob_digit)
    print(c.conver2, c.ob_size, c.ob_digit)


print("+++++++++++++++")
print(1073741823*9999)
'''

# 测试2(测试绝对值减法): 利用Python 构造两个超大的数,进行大整数运算(连加得到的数字大小有限)
'''
a = -99999999999999999999999 * 1231231231231231231237843653274612123
# p = a * -3
n = PyLongObject()
v = PyLongObject()
m = PyLongObject().conver(a)
# n = PyLongObject().conver(p)

print(m.ob_digit)
print(m.ob_size)
print(n.ob_digit)
print(n.ob_size)

q = n + v
print(q.ob_digit)
print(q.ob_size)
'''

# 测试三 利用减法对大整数进行计算
'''
a = PyLongObject.conver(92341267896345634634334526)
b = PyLongObject.conver(-83671636456362623643653463634)
print('====')
print(a.conver2)
print(a.ob_digit)
print(a.ob_size)
print(a.conver2)
print('=====')
print(b.conver2)
print(b.ob_digit)
print(b.ob_size)


c = b - a 
print(c.conver2)
print(c.ob_digit)
print(c.ob_size)
'''

para resumir

Consulte la implementación subyacente de Cpython, el código general es un poco demasiado directo. La posible razón es que la referencia al código fuente de C conduce al código escrito de Python, que parece carecer de un gusto orientado a objetos. En uno, la implementación del código fuente C es realmente concisa y eficiente. ! La realización de la suma y resta de valor absoluto utiliza dos ciclos para manejar todas las situaciones. ¡Tan simple y suave! Durante la conversión a código Python, se probaron muchos errores durante el período. Después de resolverlos uno por uno, el lenguaje C para el ciclo i ++ se puede usar de manera muy flexible. Puede ser ++ o no ++ cuando sea necesario, mientras que Python sonó sólo puedo dejar que finalmente permanezca en el número anterior del ciclo. Además de este ciclo doble, también agregué un juicio lógico cuando se trata de operaciones que involucran 0.

Un código tan simple y eficiente, la lógica no es muy complicada, es todo código ordinario, sintaxis ordinaria. Pero creo que es difícil escribir un código tan eficiente y conciso con mi fuerza.

Supongo que te gusta

Origin blog.csdn.net/pythonstrat/article/details/114638887
Recomendado
Clasificación