Simulate python's underlying data type, the realization of large integers!

We refer to the logic of the C source code and try to use python to simulate the realization of large integers. Here only the operation of addition and subtraction is implemented.

(Thanks to the author for his column, I have gained a lot, please refer to Mukenet: "In-depth Analysis of Python Source Code")

(1) Class: Int32()

  • This is designed to imitate the 4-byte integer data of the int type .
  • Defines 4 basic operations of addition, subtraction, multiplication and division
  • Use the data descriptor to limit the data. The largest value is only -2 **31 to 2 **31 -1
  • When the operation result is greater than this range, it will throw an exception, prompting you to integer overflow
  • A conversion function, a large value when the result is converted to a large integer type is calculated (PyLongObject ())
  • It should be noted that there is no operation between Int32 and PyLongObject. Therefore, the operation is not performed in Int32, and it is directly converted internally and returned to PyLongObject, for the purpose of type confusion. The user himself calls the method to convert int32 to a large integer for calculation.

(2) Class: PyLongObject()

  • Defines big integers, and defines big integer addition and addition operations
  • Define the class method: conver , which can be called directly as a class. When the converted value of int32 is small, you can directly a = PyLongObject().conver(1231232132132131231232131231231231) to create a super large integer object. In addition, the convert() conversion function of Int32 is also implemented by calling this method.
  • The conver2 method is also defined , and the @property decorator is added . When the value of a large integer is large, the method can be called directly like a property to return the value of the python type and observe its value.

(3)类:PyLong_Type()

  • The absolute value addition and subtraction are defined, which are called when the PyLongObject() object instance performs operations .
  • In addition, the type object provides the corresponding method function, which is directly defined as a singleton , and the instance object is globally unique, saving memory overhead.

(4) Base = 2 ** 30 A global constant defined at the top. When an array represents a large integer, it represents the largest storage of each unit, which is also equivalent to a hexadecimal system. When doing basic addition and subtraction operations, it will not exceed the size represented by int32, which is convenient for calculation and is also a detail!

The above code below: (The comment part is the function code of the test program, which has no other effect)

# 这里用来实现,大整数
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)
'''

to sum up

Refer to the underlying implementation of Cpython, the overall code is a bit too blunt. The possible reason is that the reference to the C source code leads to the written python code, which seems to lack an object-oriented taste. In one, the implementation of source code C is really concise and efficient! ! The realization of absolute value addition and subtraction uses two loops to handle all situations. So simple and smooth! During the conversion to python code, many bugs were tested during the period. After solving them one by one, the C language for loop i++ can be used very flexibly. It can be ++ or not ++ when needed, while the python rang can only Let i finally remain at the previous number of the loop. In addition to this double loop, I also added a logical judgment when dealing with operations involving 0.

Such simple and efficient code, the logic is not very complicated, it is all ordinary code, ordinary syntax. But I think it is difficult to write such efficient and concise code with my strength.

Guess you like

Origin blog.csdn.net/pythonstrat/article/details/114638887