ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-03-10 17:02:40  阅读:231  来源: 互联网

标签:digit python 数据类型 ob borrow carry 底层 size


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

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

(1)类: Int32()

  • 这是设计出来的,用来模仿 int类型 4字节的整形数据。
  • 定义了加减乘除4个基本运算
  • 利用数据描述器,对数据进行限制。最大的取值只有 -2 **31 到 2 **31 -1
  • 当运算结果大于 这个范围,便会抛出异常,提示你 整数溢出
  • 提供一个转换函数,当结果取值大时, 转换为 大整数类型进行计算(PyLongObject())
  • 这里需要注意的是, Int32 和 PyLongObject 之间不能运算。 所以并未 在 Int32 做运算是,直接内部进行转换,返回 PyLongObject, 为的就是类型混淆。 用户自己调用方法,将 int32 转换为 大整形进行计算。

(2) 类:PyLongObject()

  • 定义了大整数, 并定义了 大整数 加法和 加法运算
  • 定义 类方法: conver, 可直接以类调用,在 int32 转换起来数值较小时,可直接 a = PyLongObject().conver(1231232132132131231232131231231231) , 创建一个超级大的 大整数对象。 并且,Int32convert() 转换函数,也是调用这个方法实现。
  • 也定义了 conver2 方法,加 @property装饰器, 在大整数数值较大时, 可直接 像属性一样调用该方法,返回 python 类型的数值,观察它的数值。

(3)类:PyLong_Type()

  • 定义了 绝对值加法和减法, 供PyLongObject() 对象实例进行运算时调用。
  • 另外 类型对象提供相应的方法函数,直接定义为单例,实例对象全局唯一,节省内存的开销。

(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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有