【Python】数据结构之数值类型

整数 (int)(Python 的 int 可表示任意大小的整数)

在 Python 中,整数类型(int)表示一个没有小数点的数字,支持正数、负数以及零。

1. 整数的定义

在 Python 中,整数定义的语法非常简单:

x = 5  # 定义一个正整数
y = -10  # 定义一个负整数
z = 0  # 定义一个零

2. 整数的表示方式

Python 中的整数可以通过十进制、二进制、八进制和十六进制等多种方式表示。

  • 十进制

    num = 10  # 十进制
    
  • 二进制:以 0b0B 为前缀

    num = 0b1010  # 二进制 1010 表示十进制 10
    
  • 八进制:以 0o0O 为前缀

    num = 0o12  # 八进制 12 表示十进制 10
    
  • 十六进制:以 0x0X 为前缀

    num = 0xA  # 十六进制 A 表示十进制 10
    

3. 整数的内存表现

内存管理

  • 在 Python 3 中,整数类型采用了 大整数(arbitrary precision) 的实现,也就是说,整数的大小仅受限于可用内存,而不再有固定的范围(如 C 语言中的 int 类型)。
  • 在早期的 Python 版本(Python 2.x)中,整数有两种类型:intlong,其中 long 用于表示非常大的整数,但在 Python 3 中,所有的整数类型都合并为 int,并且可以表示任意大小的整数。

内存占用

  • Python 中整数的内存占用是动态的,具体占用多少内存取决于整数的大小。在 Python 3 中,小整数(通常为 -5 到 256 之间的整数)会被内部缓存,而 大整数 会根据其值动态分配内存。

4. 整数类型的范围

Python 3 中的整数类型没有最大值限制。Python 会自动管理大整数所占的内存空间,使其可以处理非常大的整数,唯一的限制是可用内存。例如:

large_number = 10**100  # 一个非常大的整数,包含 101 个数字
print(large_number)

由于 Python 内部采用了 可变长度的整数 表示方式,它能够动态增长,以适应更大的数字。


5. 大整数运算的优化

Python 的整数类型没有固定的大小限制,因此,当涉及到大数运算时,Python 会自动根据整数的大小分配更多的内存。由于整数的表示是动态调整的,大整数的运算比小整数慢,主要是因为大整数需要更多的计算资源来处理它们的每一位。

例如,10**1000 是一个包含1000位数的大整数。计算大整数时,Python 会使用更加复杂的算法,如 Karatsuba 算法快速傅里叶变换(FFT) ,来优化乘法和除法的计算。


6. 整数缓存机制

Python 内部对小整数(通常是从 -5 到 256 的整数)采用了缓存机制,也就是说,这些小整数在 Python 程序启动时就被创建,并存储在内存中。因此,对于这些小整数的重复使用,性能上不会有太大开销。这一优化减少了内存分配的开销,提高了程序的执行速度。


浮点数 (float)(支持科学计数法)

浮点数表示的是具有小数部分的数字,可以使用带小数点的数字或者科学计数法表示。Python 中的浮点数类型是 float,在内部,它是通过 双精度浮点数(double precision floating point number) 来表示的,这符合 IEEE 754 标准。

1. 浮点数的定义

你可以通过以下几种方式定义浮点数:

# 直接使用带小数点的数字
a = 3.14  # 定义一个浮点数

# 使用科学计数法(exponential notation)
b = 1.23e4  # 等同于 1.23 * 10^4,即 12300.0
c = 4.56e-2  # 等同于 4.56 * 10^-2,即 0.0456

# 科学计数法和普通浮点数之间的转换
print(float('3.14159'))  # 3.14159

2. 浮点数在内存中的表示

Python 中的浮点数是通过 IEEE 754 双精度浮点数(64 位) 来表示的。根据 IEEE 754 标准,浮点数由三个部分组成:

  1. 符号位:1 位,表示数值的符号,0 表示正数,1 表示负数。
  2. 指数位:11 位,用于表示浮点数的指数。
  3. 尾数位:52 位,用于表示浮点数的有效数字(小数部分)。

因此,浮点数在内存中的大小固定为 64 位。


3. 浮点数的范围

在 Python 中,浮点数的最大范围大约为 1.8 × 10^308,最小范围为 5.0 × 10^-324(接近 0)。这些数字的精度依赖于计算机的硬件支持和 Python 的浮点数实现。


4. 浮点数的精度与舍入

浮点数在计算机中并不是精确存储的,尤其是那些无法准确表示为二进制的小数。例如,0.1 在计算机内部的表示可能是一个近似值。

print(0.1 + 0.2)  # 输出 0.30000000000000004

这意味着,浮点数在计算过程中可能会遇到微小的精度误差。这是由于浮点数的有限精度和二进制表示法的局限性。


5. 浮点数比较

由于浮点数精度的问题,比较浮点数时不能直接使用 == 来判断它们是否相等。更好的方式是使用一个 容差范围,通过判断它们之间的差值是否小于一个非常小的值来比较。

a = 0.1 + 0.2
b = 0.3

# 不推荐直接使用 == 来比较
print(a == b)  # False

# 使用容差范围比较
print(abs(a - b) < 1e-9)  # True

6. 性能优化

在 Python 中,浮点数的运算速度通常较整数稍慢,因为浮点数计算需要更多的硬件支持,例如浮点单元(FPU)。对于大规模的浮点数运算,性能瓶颈可能会出现在计算过程中,尤其是当运算涉及高精度计算时,误差累积可能导致性能降低。

import time

# 测量浮点数运算的时间
start = time.time()
result = sum([0.1] * 1000000)
end = time.time()

print(f"运算时间:{end - start}秒")

7. decimal 模块:提高精度

如果需要高精度的浮点运算,可以使用 decimal 模块,它提供了任意精度的小数表示,可以避免常规浮点数的精度误差。

from decimal import Decimal

a = Decimal('0.1')
b = Decimal('0.2')

print(a + b)  # 0.3

复数 (complex)(形如 a + bj

1. 复数的基本概念

复数的标准形式是:

z = a + b i z = a + bi z=a+bi

其中:

  • a (实部) :普通的实数
  • b (虚部) :实数,乘以 i(数学中的虚数单位)
  • i (虚数单位) :满足 i 2 = − 1 i^2 = -1 i2=1

Python 采用 j 作为 虚数单位(而不是数学中的 i)。


2. 复数的定义

Python 复数的定义方式有:

# 直接使用 a + bj 的方式定义复数
z1 = 3 + 4j  
z2 = -2.5 + 0.5j  

print(type(z1))  # 
print(z1)        # (3+4j)
print(z2)        # (-2.5+0.5j)

在 Python 中,j 必须与数值直接相连,不能有空格,比如 3 + 4 j 会报错。


3. 使用 complex(real, imag) 构造函数
z3 = complex(3, 4)   # 等同于 3 + 4j
z4 = complex(-2.5, 0.5)  # 等同于 -2.5 + 0.5j

print(z3)  # (3+4j)
print(z4)  # (-2.5+0.5j)

4. 复数的基本属性

Python 提供了几个属性来获取复数的实部、虚部和共轭复数

z = 3 + 4j

# 获取实部
print(z.real)  # 3.0

# 获取虚部
print(z.imag)  # 4.0

# 获取共轭复数
print(z.conjugate())  # (3-4j)

共轭复数(Conjugate)是 a - bi,即虚部取反的复数。


5. 复数的运算
复数的加减法
z1 = 2 + 3j
z2 = 1 - 4j

print(z1 + z2)  # (3-1j)
print(z1 - z2)  # (1+7j)

计算方式:

( 2 + 3 j ) + ( 1 − 4 j ) = ( 2 + 1 ) + ( 3 − 4 ) j = 3 − j (2+3j) + (1-4j) = (2+1) + (3-4)j = 3 - j (2+3j)+(14j)=(2+1)+(34)j=3j

( 2 + 3 j ) − ( 1 − 4 j ) = ( 2 − 1 ) + ( 3 + 4 ) j = 1 + 7 j (2+3j) - (1-4j) = (2-1) + (3+4)j = 1 + 7j (2+3j)(14j)=(21)+(3+4)j=1+7j


复数的乘法
z1 = 2 + 3j
z2 = 1 - 4j

print(z1 * z2)  # (14-5j)

计算方式:

( 2 + 3 j ) × ( 1 − 4 j ) = 2 × 1 + 2 × ( − 4 j ) + 3 j × 1 + 3 j × ( − 4 j ) (2+3j) \times (1-4j) = 2 \times 1 + 2 \times (-4j) + 3j \times 1 + 3j \times (-4j) (2+3j)×(14j)=2×1+2×(4j)+3j×1+3j×(4j)

= 2 − 8 j + 3 j − 12 j 2 = 2 - 8j + 3j - 12j^2 =28j+3j12j2

= 2 − 8 j + 3 j + 12 ( 因为  j 2 = − 1 ) = 2 - 8j + 3j + 12 \quad (\text{因为 } j^2 = -1) =28j+3j+12(因为 j2=1)

= 14 − 5 j = 14 - 5j =145j


复数的除法
z1 = 2 + 3j
z2 = 1 - 4j

print(z1 / z2)  # (-0.5882352941176471+0.6470588235294118j)

计算公式:

a + b i c + d i = ( a + b i ) × ( c − d i ) c 2 + d 2 \frac{a + bi}{c + di} = \frac{(a+bi) \times (c-di)}{c^2 + d^2} c+dia+bi=c2+d2(a+bi)×(cdi)

对于 z1 = 2 + 3jz2 = 1 - 4j

( 2 + 3 j ) × ( 1 + 4 j ) 1 2 + ( − 4 ) 2 = ( 2 + 3 j ) × ( 1 + 4 j ) 17 \frac{(2+3j) \times (1+4j)}{1^2 + (-4)^2} = \frac{(2+3j) \times (1+4j)}{17} 12+(4)2(2+3j)×(1+4j)=17(2+3j)×(1+4j)

≈ − 0.588 + 0.647 j \approx -0.588 + 0.647j 0.588+0.647j


复数的指数运算
z = 1 + 1j
print(z ** 2)  # 2j

计算方式:

( 1 + 1 j ) 2 = 1 + 2 j + j 2 = 1 + 2 j − 1 = 2 j (1+1j)^2 = 1 + 2j + j^2 = 1 + 2j - 1 = 2j (1+1j)2=1+2j+j2=1+2j1=2j


计算复数的模

复数的模(Magnitude)定义为:

∣ z ∣ = a 2 + b 2 |z| = \sqrt{a^2 + b^2} z=a2+b2

Python 使用 abs() 计算模:

import cmath

z = 3 + 4j
print(abs(z))  # 5.0

计算方式:

3 2 + 4 2 = 9 + 16 = 25 = 5 \sqrt{3^2 + 4^2} = \sqrt{9+16} = \sqrt{25} = 5 32+42 =9+16 =25 =5


计算复数的相角

复数的相角(Argument)是从正实轴到复数 z 的夹角,单位为 弧度

z = 3 + 4j
print(cmath.phase(z))  # 0.9272952180016122

计算方式:

θ = tan ⁡ − 1 ( b a ) = tan ⁡ − 1 ( 4 3 ) ≈ 0.93 (弧度) \theta = \tan^{-1}(\frac{b}{a}) = \tan^{-1}(\frac{4}{3}) \approx 0.93 \text{(弧度)} θ=tan1(ab)=tan1(34)0.93(弧度)


计算复数的极坐标

Python 提供了 cmath.polar() 来计算极坐标:

z = 3 + 4j
print(cmath.polar(z))  # (5.0, 0.9272952180016122)

返回 (r, θ),其中 r 是模,θ 是相角。

可以使用 cmath.rect(r, θ) 将极坐标转换回复数:

r, theta = cmath.polar(z)
print(cmath.rect(r, theta))  # (3+4j)

6. 复数运算的性能分析

Python 复数运算相较于 整数和浮点数 更消耗内存和 CPU 资源,主要原因:

  1. 每次计算都会创建新对象
  2. 涉及额外的虚部计算
  3. Python 的动态类型管理增加了额外开销

我们可以使用 timeit 测试复数运算与浮点数运算的性能:

import timeit

float_time = timeit.timeit("x = 3.0 * 4.0", number=1000000)
complex_time = timeit.timeit("x = (3.0 + 4.0j) * (2.0 - 1.0j)", number=1000000)

print("浮点数运算时间:", float_time)
print("复数运算时间:", complex_time)

输出:

浮点数运算时间: 0.057 秒
复数运算时间: 0.122 秒

复数运算比浮点数运算慢约 2 倍,因为:

  • 复数计算涉及两次浮点运算(实部、虚部)。
  • 需要 Python 进行类型管理和动态内存分配。

7. 复数 vs. 其他数据类型的内存占用

我们可以比较 整数、浮点数、复数 的内存占用:

import sys

x_int = 42
x_float = 3.14
x_complex = 3 + 4j

print(sys.getsizeof(x_int))    # 整数的大小(28 字节)
print(sys.getsizeof(x_float))  # 浮点数的大小(24 字节)
print(sys.getsizeof(x_complex))  # 复数的大小(32 字节)

输出

整数大小: 28 字节
浮点数大小: 24 字节
复数大小: 32 字节
  • 复数占用 32 字节,比浮点数(24 字节)多 8 字节,因为复数包含两个 double(实部和虚部)。
  • 整数的存储结构更复杂,导致比浮点数占用更多内存

布尔值 (bool)(TrueFalse,本质是 int 的子类)

Python 的 布尔类型(Boolean, bool 是 Python 的基本数据类型之一,用于表示 True (真)和 False (假) 两种状态。布尔类型在逻辑运算、条件判断、控制流等方面起着至关重要的作用。

1. 布尔类型的基本定义

在 Python 中,布尔类型是 bool,只有两个值:

  • True(真)
  • False(假)
a = True
b = False

print(type(a))  # 
print(type(b))  # 
  • TrueFalse 在 Python 内部实际上是 整数的子类boolint 的子类)。
  • True 的值相当于 1False 的值相当于 0

2. 布尔类型的本质

Python 中的 bool 实际上是 整数的子类

print(issubclass(bool, int))  # True

这意味着 布尔值可以用于数值运算

print(True + 1)   # 2
print(False + 1)  # 1
print(True * 10)  # 10
  • True 在数学运算中相当于 1
  • False 在数学运算中相当于 0

即:

True == 1  # True
False == 0  # True

但要注意:

True is 1  # False
False is 0  # False

虽然 True == 1,但 True1不同的对象,在内存中存储的位置不同。


3. 布尔类型的逻辑运算

Python 提供了 3 种逻辑运算符:

  • and (与) :只有两个操作数都为 True,结果才是 True
  • or (或) :只要有一个操作数为 True,结果就是 True
  • not (非) :取反,not TrueFalsenot FalseTrue

示例:

print(True and False)  # False
print(True or False)   # True
print(not True)        # False

4. 布尔类型的短路运算

Python 的 andor 具有**短路(lazy evaluation)**特性:

  • and 遇到 False 就直接返回,不再计算后面的表达式。
  • or 遇到 True 就直接返回,不再计算后面的表达式。
x = False and print("不会执行")  # `False` 遇到 `and`,后面的 print() 不会执行
y = True or print("不会执行")   # `True` 遇到 `or`,后面的 print() 不会执行

5. 布尔类型的隐式转换

Python 在条件判断、循环、逻辑运算时,自动将非布尔类型转换为 TrueFalse

**False** 、****0 、****0.0 、以及各种空的数据结构外,其余都为**True**

数据类型 转换结果
布尔类型 True True
False False
整数 0 False
任何非零整数 True
浮点数 0.0 False
任何非零浮点数 True
复数 0j False
任何非零复数 True
字符串 ""(空字符串) False
"hello"(非空字符串) True
列表 [](空列表) False
[1, 2, 3](非空列表) True
元组 ()(空元组) False
(1, 2, 3)(非空元组) True
字典 {}(空字典) False
{"a": 1}(非空字典) True
集合 set()(空集合) False
{1, 2, 3}(非空集合) True
None None False
自定义类 __bool__()返回False False
__bool__()返回True True
__len__()返回0 False
__len__()返回>0 True

6. 布尔运算的性能

在 Python 中,TrueFalse 作为单个整数对象存储,它们的计算速度非常快

import timeit

time1 = timeit.timeit("10 < 20", number=1000000)  # 0.03 秒
time2 = timeit.timeit("1 == 1", number=1000000)   # 0.02 秒

print(time1, time2)

布尔运算通常比字符串或列表等数据类型的操作快得多,因为:

  1. 布尔值存储在整数的最低位,计算快
  2. 逻辑运算可以短路,减少计算步骤
  3. CPU 原生支持布尔运算,因为对于CPU的每一次计算而言,其实都是布尔运算,CPU计算是通过电路的通路闭路来计算

数值类型转换(int()float()complex()bool()

Python 提供了多种数值类型转换方式,包括隐式转换(自动类型转换)和显式转换(手动转换) 。这些转换在算术运算、函数调用和存储数据时至关重要。

1. 隐式转换(自动类型转换)

Python 会在合适的场景下自动进行类型转换,以避免数据精度丢失或不兼容的操作。

整数(int) -> 浮点数(float)

在数学运算中,整数和浮点数混合运算时,整数会自动转换为浮点数

a = 5    # int
b = 2.0  # float
c = a + b  # 5 + 2.0 -> 7.0(自动转换为 float)
print(c, type(c))  # 7.0 

整数(int) -> 复数(complex)

当整数与复数混合运算时,Python 自动将整数转换为复数类型。

x = 3    # int
y = 4j   # complex
z = x + y  # 3 + 4j
print(z, type(z))  # (3+4j) 

浮点数(float) -> 复数(complex)

如果浮点数和复数混合计算,浮点数自动转换为复数类型:

a = 3.5  # float
b = 2j   # complex
c = a + b  # 3.5 + 2j
print(c, type(c))  # (3.5+2j) 

注意:

  • 复数(complex)不会自动转换为其他数值类型,因为它包含虚部。
  • 例如 int(3+4j)float(3+4j) 都会报错。

2. 显式转换(手动类型转换)

显式转换是使用内置函数 int()float()complex() 进行类型转换。

转换为整数(int)

可以使用 int()浮点数、布尔值、字符串(仅包含数字) 转换为整数。

print(int(3.9))   # 3(截断小数部分)
print(int(-3.9))  # -3(截断小数部分)
print(int(True))  # 1(布尔值 True 转换为 1)
print(int(False)) # 0(布尔值 False 转换为 0)
print(int("42"))  # 42(字符串转整数)

注意:

  • int() 不会四舍五入,而是直接截断小数部分。

  • 如果字符串中包含非数字字符(如 "42a"),会报错:

    int("42a")  # ValueError: invalid literal for int()
    

转换为浮点数(float)

可以使用 float() 将整数、布尔值、字符串(仅包含数字)转换为浮点数。

print(float(5))       # 5.0
print(float(True))    # 1.0
print(float(False))   # 0.0
print(float("3.14"))  # 3.14

注意:

  • 布尔值 True 转换为 1.0False 转换为 0.0
  • float("3.14a") 会报错,因为 "3.14a" 不是一个有效的数字。

转换为复数(complex)

complex() 可以将整数、浮点数和字符串转换为复数。

print(complex(5))        # (5+0j)
print(complex(2.5))      # (2.5+0j)
print(complex("3.14"))   # (3.14+0j)
print(complex(2, 3))     # (2+3j)(指定实部和虚部)

注意:

  • complex() 不支持布尔值complex(True) 会报错)。
  • complex("3+4j") 也会报错,字符串必须是单独的数字,如 "3.14"

你可能感兴趣的:(数据结构,python)