编程语言
首页 > 编程语言> > 模拟python底层数据类型,大整数的实现!

模拟python底层数据类型,大整数的实现!

作者:互联网

我们参考C源码的逻辑,试着用python去模拟一下大整数的实现,这里 只实现了加减法的操作。

(感谢作者的专栏,收获很大,参考 慕课网:《Python 源码深度剖析》)

(1)类: Int32()

(2) 类:PyLongObject()

(3)类:PyLong_Type()

(4) Base = 2 ** 30 定义在顶部的一个 全局常量。 数组表示大整数的时候,代表每个单元最大的存储,也相当于是进制。 再做基本的加减法运算的时候, 不会超过 int32 表示的大小,方便计算,也是细节之处!

下面上代码:(注释部分为测试程序功能代码,没其他作用)

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

总结

参考Cpython的底层实现, 整体代码有些过度生硬,可能原因是参考C源码,导致写的 python代码,好像有些缺失面向对象的味道。 在一个 , 源码C的 实现真的是简洁高效!! 绝对值加减的实现, 用两个循环,对所有的情况进行处理。如此简洁,流畅! 在转换为 python代码的时候,期间还是测试出了很多bug,一一解决之后, C 语言的 for 循环 i++, 运用的非常灵活,可在需要时 ++ 或者不 ++, 而python的 rang 只能让 i 最终保持在 循环的 前一个数字, 在对于 有 0参与进来的运算时,python 除了这双重循环, 我还另加了一个逻辑判断。

如此简单高效的代码,逻辑并不是非常复杂, 都是普通代码,普通语法。 但我想以我的实力~ 也很难写出这么高效简洁的代码。

标签:digit,python,数据类型,ob,borrow,carry,底层,size
来源: https://blog.csdn.net/pythonstrat/article/details/114638887