# Python Roman Numeral **Repository Path**: gitiber/python-roman-numeral ## Basic Information - **Project Name**: Python Roman Numeral - **Description**: Python罗马数字类型(基本运算支持)。 - **Primary Language**: Unknown - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-07-16 - **Last Updated**: 2022-07-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Python罗马数字类型 ### 介绍 程序以面向对象的思想编写,创建新类表示罗马数字类型;该类对象拥有阿拉伯数字(整型)和对应的罗马数字(字符串型)两个实例变量,使用一个整型或字符串型值初始化,通过封装进行关键变量和方法的私用化,并对特殊方法、数值型数据常用的比较和算术运算符进行重载,以实现: 1. 阿拉伯数字与罗马数字的相互转换; 2. 罗马数字之间、罗马数字与阿拉伯数字之间的大小比较和基本算术运算。 ### 用法 #### 实例化 将类名RomanNum作为函数名调用,将 - 一个范围在1至3999之间的整型数据,或 - 一个用且仅用「I,V,X,L,C,D,M」符号集表示的、合法的罗马数字字符串,亦或 - 一个RomanNum类型的实例 作为唯一参数传入,得到一个RomanNum(罗马数字)类型的实例对象;可以将其看作是广义的数值型数据的一种。使用赋值语句将得到的实例赋值给变量。 如果传入的参数不符合规范,程序将触发异常。 #### 获取罗马数字 使用roman()公用方法,获取以罗马数字的形式表示的实例的数值。该数字会以str格式被返回。 另外,对实例调用print()或str()函数,或是在Python交互式终端中直接输入实例名并回车,RomanNum类实例也均会返回这个罗马数字字符串。 #### 获取阿拉伯数字 使用arabic()公用方法,获取以阿拉伯数字的形式表示的实例的数值。该数字会以int格式被返回。 另外,对实例调用int()函数,RomanNum类实例也会返回这个阿拉伯数字整型数据。 #### 更改数值 如果希望更改变量中存放的RomanNum类实例的数值,又不希望重新进行赋值,可以采用更简单的句法:将实例变量的变量名作为函数名调用,传入符合「实例化」小节中的规定的参数(整型数据、字符串或RomanNum类实例),变量对应的实例数值即可得到更新。 由于RomanNum类是不可变类,实际上会重新进行实例化。 #### 相等比较运算 RomanNum类实例(需在运算符左侧)可以与多种类型的值进行「==,!=」两种比较运算。 注意:①对于数值型数据(含RomanNum类),仅关注其数值而忽略其类型;②符合规范的字符串会被当作罗马数字字符串,取其对应数值参与比较。 #### 大小比较运算 RomanNum类实例(需在运算符左侧)还可以与RomanNum类实例、整型数据、浮点型数据、符合罗马数字规范的字符串进行「<,>,<=,>=」四种比较运算。 注意:①;②;③字符串不符合规范或使用其他类型的值作为运算对象时,程序将触发异常。 #### 算术运算 RomanNum类实例(需在运算符左侧)可以与RomanNum类实例、整型数据、浮点型数据、符合罗马数字规范的字符串进行「+,-,*,**,//,%」六种算术运算,并得到一个RomanNum类实例。 注意:①;②;③;④由于罗马数字的表示能力限制,数值运算的结果会被向下取整;⑤如果向下取整后的结果数值超出1至3999的范围,程序将触发异常。 #### 获取罗马数字的字符个数 对实例调用len()函数,可以获得罗马数字字符串中的字符个数。 ### 实现 #### 实例化的参数 首先,阿拉伯数字以整型数据表示,罗马数字以字符串表示。RomanNum类在实例化时,会创建__arabic和__roman两个私用变量来存放这两个值,那么就必然需要一个代表阿拉伯数字的整型数据或一个代表罗马数字的字符串来规定实例的数值(为方便叙述,认为RomanNum类实例的数值即是其__arabic的数值)。当然也应该支持使用RomanNum类实例来提供这一数值。如果传入的实例化参数并非以上三种类型,那么就raise一个TypeError吧。 由于__arabic和__roman已被封装以阻止外部修改,按照原则创建了公用属性arabic和roman来获取它们的值。对它们赋值则相当于修改实例的数值。 #### 数字转换 当实例化参数缺失阿拉伯数字或缺失罗马数字时,实例化过程中需要进行数字互换。数字互换通过调用a_to_r()和r_to_a()两个模块全局函数来实现,并需要用到一个名为__reference的私用二维元组作为参照。__reference中每个元组是一对罗马数字符号和对应的阿拉伯数字数值,按数值从大到小排列。简单起见,阿拉伯数字以4或9开头的整个、十、百也纳入其中。__reference是类变量而非实例变量,其值与实例化无关。 a_to_r():先判断要被转换的整型数据是否在1至3999范围之内,否则raise一个ValueError。然后大循环对列表中的元组进行迭代,小循环重复检查要被转换的整型数据是否大于当前迭代迭到的罗马数字符号对应的数值,是则为目标罗马数字字符串添加符号,并把要被转换的整型数据扣除对应数值。最终出循环返回目标罗马数字字符串。 r_to_a():大循环对列表中的元组进行迭代,小循环重复检查要被转换的字符串是否以当前迭代迭到的罗马数字符号开头,是则为目标阿拉伯数字整型数据加上对应数值,并把要被转换的字符串开头的切掉。然后出循环判断要被转换的字符串是否已经为空,不空则不是合法的罗马数字字符串,需raise一个ValueError。最终返回目标阿拉伯数字整型数据。 #### 基础方法重载 重载__call__()以实现变量中的实例的数值更改;我们希望RomanNum类是不可变类,因此直接使用__init__()即可。 重载__str__()和__repr__()使其返回__roman,便于显示。 重载__int__()和__index__()使其返回__arabic,便于获取数值。 重载__len__(),干啥用好呢?返回__roman的长度吧。 #### 运算符重载 重载「==」运算符,右侧支持尽可能多的类型。右侧的RomanNum类实例取其__arabic、整型或浮点型数据直接与左侧实例的__arabic比较;这样右侧的数值便不受罗马数字的表示范围的限缩。右侧的字符串则直接与左侧的__roman比较;这样右侧的罗马数字字符串不必被用户手动实例化成RomanNum类,就可以方便地比较大小;但实际上程序此处并不是通过自动将右侧字符串实例化来实现,而是通过左侧取__roman,这是为了确保不符合规范的字符串也不会触发异常。 重载「!=」运算符,取等于的非。 重载其他比较运算符,右侧只支持可解释出数值的类型。右侧的整型或浮点型数据直接与左侧实例的__arabic比较。右侧的其他类数据则先尝试转化成RomanNum类实例,再取其__arabic与左侧实例的__arabic比较;如果右侧无法解释出数值,实例化就会失败,触发异常。 重载算术运算符,右侧只支持可解释出数值的类型。右侧解释出的数值与左侧实例的__arabic进行算术运算后,将结果向下取整并尝试转化成RomanNum类实例;这样结果被保存在RomanNum类实例中,方便其提供自身的罗马数字字符串、再度发起RomanNum类运算,避免了手动进行实例化的麻烦。 ### 已知问题 1. 由于罗马数字符号与表示体系的问题,只用「I,V,X,L,C,D,M」符号集最大就只能表示到3999了,而且不能表示0,而且不能表示小数。所以算术运算时一来非常容易溢出,二来只能把结果向下取整。如果希望得到大整数、非正整数、精确的浮点数,或者单纯不需要该类实例的结果,建议对运算对象中的RomanNum类实例调用int()函数,取其数值后参与计算。 2. 由于只在RomanNum类的定义中重载了运算符,有RomanNum类实例参与的运算,它必须被写在运算符左侧。 3. 由于算术运算的结果会被向下取整,实际上除法也就成了整除。在两者的功能完全等同的情况下,仅重载了原意义更贴近实际效果,但在一般程序编写中使用度更低的整除。