首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 中有关数字必知的三件事

Python 中有关数字必知的三件事

作者头像
老齐
发布2022-01-27 14:17:38
发布2022-01-27 14:17:38
4620
举报
文章被收录于专栏:老齐教室老齐教室

如果用 Python 写过代码,肯定遇到了数字,比如整数作为列表的索引,用浮点数表示当前财富的数量,等等。

但是,Python 中关于数字的知识远多于这些。

1. 数字有方法

Python 中万物皆对象,正如在很多教材中都会看到的那样,几乎是作为第一个对象讲授的字符串对象 str ,一定查看过它的方法,比如将所有字母变为小写的 .lower() 方法。

代码语言:javascript
复制
>>> "HELLO".lower()
'hello'

毋庸置疑,数字也是 Python对象,那么它们必然也有自己的方法,比如可以用 .to_bytes() 方法将整数转化为字节字符串。

代码语言:javascript
复制
>>> n = 255
>>> n.to_bytes(length=2, byteorder="big")
b'\x00\xff'

其中参数 length 用来指定字节长度,参数 byteorder 定义自己的顺序,在上面的示例中,byteorder="big" 则在返回的字节字符串中,重要的字节排在前面,反之,则可以 byteorder="little"

255 是 8 位整数中最大的,所以,也可以将 length 设置为 1

代码语言:javascript
复制
>>> n.to_bytes(length=1, byteorder="big")
b'\xff'

但是,如果对于 256 ,也使用 length=1 ,就会出现 OverflowError

代码语言:javascript
复制
>>> n = 256
>>> n.to_bytes(length=1, byteorder="big")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: int too big to convert

也可以使用 int 的类方法 .from_bytes() 将字节字符串转化为整数:

代码语言:javascript
复制
>>> int.from_bytes(b'\x06\xc1', byteorder="big")
1729

除了整数,浮点数也有方法,例如最常用的 .is_integer() ,用于判断浮点数是否有非零的小数部分。

代码语言:javascript
复制
>>> n = 2.0
>>> n.is_integer()
True

>>> n = 3.14
>>> n.is_integer()
False

还有一个有趣的浮点数方法 .as_integer_ratio() ,它返回一个元组,其中包括浮点数所对应的分数的分子和分母,或者你可以理解为,用这个方法把浮点数转化为了分数。

代码语言:javascript
复制
>>> m = 2.5
>>> m.as_integer_ratio()
(5, 2)

当然,由于二进制转化的时候有误差(详见《Python大学实用教程》中的解释),也会出现下面的结果。

代码语言:javascript
复制
>>> m = 0.1
>>> m.as_integer_ratio()
(3602879701896397, 36028797018963968)

特别要注意,上面在使用整数对象的方法时,都是使用了变量引用该整数对象,如果这样:

代码语言:javascript
复制
>>> 255.to_bytes(length=1, byteorder="big")
  File "<stdin>", line 1
    255.to_bytes(length=1, byteorder="big")
        ^
SyntaxError: invalid syntax

解决方法是:

代码语言:javascript
复制
>>> (255).to_bytes(length=1, byteorder="big")
b'\xff'

但是,对于浮点数,则又不同:

代码语言:javascript
复制
>>> 3.14.is_integer()
False
>>> (3.14).is_integer()
False

注意区分。

2. 数字有层级

数学上的数字都有层级,比如所有的自然数都是整数,所有的整数都是有理数,所有的有理数都是实数,所有的实数都是复数。

Python 中的数字,也有类似的金字塔结构。

层级继承

Python 中的所有数字,都是 Number 类的实例:

代码语言:javascript
复制
>>> from numbers import Number
>>> isinstance(1992, Number)
True
>>> isinstance(7.28, Number)
True
>>> isinstance(1j, Number)
True

如果只关心某个对象是否是数字,而不在乎它是哪类数值,就可以使用 isinstance(value, Number) 进行检验。

numbers 模块中,有四个类:ComplexRealRationalTntegral ,它们分别对应 Python 内置对象中的相应类型:

  • Complex 类用于表示复数。内置对象的 Complex 类型:complex
  • Real 类用于表示实数。内置对象的 Real 类型:float
  • Rotional 类用于表示有理数。内置对象的 Roational 类型:Fraction
  • Integral 类用于表示整数。内置独享的 Integral 类型:intbool

没错,bool 的值也是数字。

代码语言:javascript
复制
>>> import numbers

# 复数继承自 Complex
>>> isinstance(1j, numbers.Complex)
True

# 复数不是 Real
>>> isinstance(1j, numbers.Real)
False

# 浮点数是 Real
>>> isinstance(3.14, numbers.Real)
True

# 浮点数不是 Rational
>>> isinstance(3.14, numbers.Rational)
False

# 分数是 Rational
>>> from fractions import Fraction
>>> isinstance(Fraction(1, 2), numbers.Rational)
True

# 分数不是 Integral
>>> isinstance(Fraction(1, 2), numbers.Integral)
False


# 整数是 Integral
>>> isinstance(1729, numbers.Integral)
True

# 布尔值是 Integral
>>> isinstance(True, numbers.Integral)
True

>>> True == 1
True

>>> False == 0
True

小数不在其列

在上面所说的 Python 中四种数字层级:ComplexRealRationalTntegral ,其中不包含 Decimal 类型,它比较特殊,算是第五级。

数学上,小数是实数,但是在 Python 中,不要认为 Decimal 类型的数是 Real 类型。

代码语言:javascript
复制
>>> from decimal import Decimal
>>> import numbers

>>> isinstance(Decimal("3.14159"), numbers.Real)
False

实际上,Decimal 类型的对象,仅继承了 Number 类。

代码语言:javascript
复制
>>> isinstance(Decimal("3.14159"), numbers.Complex)
False

>>> isinstance(Decimal("3.14159"), numbers.Rational)
False

>>> isinstance(Decimal("3.14159"), numbers.Integral)
False

>>> isinstance(Decimal("3.14159"), numbers.Number)
True

奇怪的浮点数

浮点数是 Real 类型,并表示实数,但是,由于内存限制,浮点数只是实数的近似值,例如:

代码语言:javascript
复制
>>> 0.1 + 0.1 + 0.1 == 0.3
False

此外,在 Python 中,float("inf")float("nan") 也都是比较特殊的浮点数对象——不是数字的数。

3. 数字可扩展

利用 Python 中关于数字的类型,比如 numbers 中的类型,可以定义其他有特殊属性和方法的数字对象。

例如,下面所定义的 ExtendedInteger 类,就代表了形如 a+b\sqrt{p} 的数字对象,其中 ab 是整数,p 不强制,默认为 2

代码语言:javascript
复制
import math
import numbers

class ExtendedInteger(numbers.Real):
    
    def __init__(self, a, b, p = 2) -> None:
        self.a = a
        self.b = b
        self.p = p
        self._val = a + (b * math.sqrt(p))
    
    def __repr__(self):
        return f"{self.__class__.__name__}({self.a}, {self.b}, {self.p})"
    
    def __str__(self):
        return f"{self.a} + {self.b}√{self.p}"
    
    def __trunc__(self):
        return int(self._val)
    
    def __float__(self):
        return float(self._val)
    
    def __hash__(self):
        return hash(float(self._val))
    
    def __floor__(self):
        return math.floot(self._val)
    
    def __ceil__(self):
        return math.ceil(self._val)
    
    def __round__(self, ndigits=None):
        return round(self._val, ndigits=ndigits)
    
    def __abs__(self):
        return abs(self._val)
    
    def __floordiv__(self, other):
        return self._val // other
    
    def __rfloordiv__(self, other):
        return other // self._val
    
    def __truediv__(self, other):
        return self._val / other
    
    def __rtruediv__(self, other):
        return other / self._val
    
    def __mod__(self, other):
        return self._val % other
        
    def __rmod__(self, other):
        return other % self._val
    
    def __lt__(self, other):
        return self._val < other
    
    def __le__(self, other):
        return self._val <= other
    
    def __eq__(self, other):
        return float(self) == float(other)
    
    def __neg__(self):
        return ExtendedInteger(-self.a, -self.b, self.p)
    
    def __pos__(self):
        return ExtendedInteger(+self.a, +self.b, self.p)
    
    def __add__(self, other):
        if isinstance(other, ExtendedInteger):
            # If both instances have the same p value,
            # return a new ExtendedInteger instance
            if self.p == other.p:
                new_a = self.a + other.a
                new_b = self.b + other.b
                return ExtendedInteger(new_a, new_b, self.p)
            # Otherwise return a float
            else:
                return self._val + other._val
        # If other is integral, add other to self's a value
        elif isinstance(other, numbers.Integral):
            new_a = self.a + other
            return ExtendedInteger(new_a, self.b, self.p)
        # If other is real, return a float
        elif isinstance(other, numbers.Real):
            return self._val + other._val
        # If other is of unknown type, let other determine
        # what to do
        else:
            return NotImplemented
    
    def __radd__(self, other):
        # Addition is commutative so defer to __add__
        return self.__add__(other)
    
    def __mul__(self, other):
        if isinstance(other, ExtendedInteger):
            # If both instances have the same p value,
            # return a new ExtendedInteger instance
            if self.p == other.p:
                new_a = (self.a * other.a) + (self.b * other.b * self.p)
                new_b = (self.a * other.b) + (self.b * other.a)
                return ExtendedInteger(new_a, new_b, self.p)
            # Otherwise, return a float
            else:
                return self._val * other._val
        # If other is integral, multiply self's a and b by other
        elif isinstance(other, numbers.Integral):
            new_a = self.a * other
            new_b = self.b * other
            return ExtendedInteger(new_a, new_b, self.p)
        # If other is real, return a float
        elif isinstance(other, numbers.Real):
            return self._val * other
        # If other is of unknown type, let other determine
        # what to do
        else:
            return NotImplemented
    
    def __rmul__(self, other):
        # Multiplication is commutative so defer to __mul__
        return self.__mul__(other)
    
    def __pow__(self, exponent):
        return self._val ** exponent
    
    def __rpow__(self, base):
        return base ** self._val

上面所定义的数字类型,可以这样使用:

代码语言:javascript
复制
>>> a = ExtendedInteger(1, 2)
>>> b = ExtendedInteger(2, 3)

>>> a
ExtendedInteger(1, 2, 2)

>>> # Check that a is a Number
>>> isinstance(a, numbers.Number)
True

>>> # Check that a is Real
>>> isinstance(a, numbers.Real)
True

>>> print(a)
1 + 2√2

>>> a * b
ExtendedInteger(14, 7, 2)

>>> print(a * b)
14 + 7√2

>>> float(a)
3.8284271247461903

Python 中的继承功能,让我们能灵活地定义各种对象。

结论

由上面所述,可知 Python 中的数字还是可以深入研究一番的。

参考资料

[1]. David Amos, 3 Things You Might Not Know About Numbers in Python

[2]. 齐伟,Python大学实用教程,北京:电子工业出版社

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 数字有方法
  • 2. 数字有层级
    • 层级继承
    • 小数不在其列
    • 奇怪的浮点数
  • 3. 数字可扩展
  • 结论
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档