Python数据类型与运算符:深入理解Python世界的基石

前言

在第一篇博客中,我们完成了 Python 环境的搭建,并写下了第一行代码 print("Hello, World!")。但那只是冰山一角。如果你只停留在 print 上,Python 能做的事 99% 都还没接触到。

本篇我们将系统学习 Python 的数据类型运算符 。这是 Python 编程的地基------任何有意义的程序,本质上都是在操作数据。数字要参与计算,文本要拼接裁剪,逻辑要判断真假,列表要增删改查。这些都离不开数据类型和运算符。

本文面向零基础读者,但讲解深度瞄准 "不仅会用,更要理解为什么这样用"。每个知识点都配有代码示例,并逐步加入 "踩坑提醒" 和 "原理剖析",帮助你建立对 Python 世界的完整认知。

1、Python 的数据类型全景图

1.1、一切皆为对象

在正式介绍数据类型之前,我们先建立一条重要认知:Python 中一切皆为对象

这不是一句营销口号,而是一个技术事实。在 Python 中,整数 42 是一个对象,字符串 "hello" 是一个对象,函数是一个对象,甚至一个类本身也是一个对象。每个对象在内存中都有以下三个特征:

  • 身份(Identity) :对象在内存中的地址,可以用 id() 函数获取。你可以把它想象成对象的 "身份证号"
  • 类型(Type) :对象是什么种类,用 type() 函数查看。就像 "人" 是一种类型,"狗" 是另一种类型
  • 值(Value):对象保存的具体数据。比如整数对象 42 的值就是 42

让我们用代码来验证这一点:

python 复制代码
# 一切皆为对象------我们来验证
a = 42

# type() 查看对象类型
print(type(a))    # 输出:<class 'int'>
# 解释:<class 'int'> 表示这是一个"整数类"(int = integer)的对象
# 在 Python 里,类型用 class(类)来表示

# id() 查看对象在内存中的地址(身份)
print(id(a))      # 输出:140734...(每次运行都不一样,这是计算器分配的内存地址)
# 解释:这个数字是 Python 解释器为 a 这个变量在内存中分配的房间号

# isinstance() 判断一个对象是否属于某种类型
print(isinstance(a, int))      		# 输出:True(42 是整数类型)
print(isinstance(a, str))      		# 输出:False(42 不是字符串类型)
print(isinstance(a, (int, float)))  # 输出:True(是 int 或 float 之一)

这段代码的输出类似于:

复制代码
<class 'int'>
140734819000000
True
False
True

为什么要知道这些? 理解 "一切皆为对象",你就能理解为什么 Python 的数据类型系统如此统一 ------ 整数、字符串、列表、函数甚至类本身,都可以赋值给变量、作为函数参数、放入列表中。没有任何 "基本类型" 和 "引用类型" 的割裂感,这使得 Python 代码写起来非常自然。

1.2、不可变类型 vs 可变类型------Python 最重要的区分

Python 的数据类型可以分为两大类:不可变类型可变类型。这个区分非常重要,它直接影响赋值、传参、比较等行为的逻辑。新手经常在这里踩坑,所以我们重点讲解。

1.2.1、什么是不可变类型?

不可变类型(Immutable) :对象创建后,其值不能被修改。如果 "修改" 了,实际上是创建了一个新对象,变量指向了新对象。

常见不可变类型:int(整数)、float(浮点数)、str(字符串)、bool(布尔值)、tuple(元组)。

1.2.2、什么是可变类型?

可变类型(Mutable):对象创建后,值可以被原地修改。多个变量可以指向同一个对象,其中一个修改了,其他变量 "看到" 的值也会变。

常见可变类型:list(列表)、dict(字典)、set(集合)。

1.2.3、用生活例子理解

想象你有一张纸条(变量),上面写着一个电话号码(值):

  • 不可变类型就像:你把纸条上的号码改掉了,纸条还是那张纸条,但号码换了。原来的号码还在那里(可能被回收了),但你的纸条现在指向新号码了。
  • 可变类型就像:你有一本通讯录,多个人都在用同一本通讯录。你在通讯录里加了一个联系人,大家下次翻开这本通讯录,都能看见你加的联系人------因为大家用的是同一本通讯录,不是各自的拷贝。

1.2.4、代码验证

python 复制代码
# ========== 不可变类型示例:int ==========
print("===== 不可变类型:int =====")
a = 100        # 变量 a 指向整数对象 100
b = a          # 变量 b 也指向 100(和 a 指向同一个对象)
print(f"修改前:a = {a}, b = {b}")
print(f"a 的内存地址:{id(a)},b 的内存地址:{id(b)}")  # 相同!

a = 200        # 修改 a,让 a 指向新的整数对象 200
print(f"修改后:a = {a}, b = {b}")
# 注意:b 还是 100!因为 a 重新指向了新对象,b 没有受影响
print(f"a 的内存地址:{id(a)},b 的内存地址:{id(b)}")  # 不同了!

# 输出:
# 修改前:a = 100, b = 100
# a 的内存地址:140734819000000,b 的内存地址:140734819000000
# 修改后:a = 200, b = 100
# a 的内存地址:140734819000200,b 的内存地址:140734819000000

print()
print("===== 可变类型:list =====")
# ========== 可变类型示例:list ==========
lst1 = [1, 2, 3]  # 创建列表 [1, 2, 3],lst1 指向它
lst2 = lst1        # lst2 也指向同一个列表对象(没有创建新列表!)
print(f"修改前:lst1 = {lst1}, lst2 = {lst2}")
print(f"lst1 的内存地址:{id(lst1)},lst2 的内存地址:{id(lst2)}")  # 相同!

lst1.append(4)     # 通过 lst1 修改列表,在末尾添加 4
print(f"修改后:lst1 = {lst1}, lst2 = {lst2}")
# 注意:lst2 也变成了 [1, 2, 3, 4]!因为 lst2 和 lst1 指向同一个列表
print(f"lst1 的内存地址:{id(lst1)},lst2 的内存地址:{id(lst2)}")  # 还是相同!

# 输出:
# 修改前:lst1 = [1, 2, 3], lst2 = [1, 2, 3]
# lst1 的内存地址:140734819000300,lst2 的内存地址:140734819000300
# 修改后:lst1 = [1, 2, 3, 4], lst2 = [1, 2, 3, 4]  ← lst2 被影响了!
# lst1 的内存地址:140734819000300,lst2 的内存地址:140734819000300

这就是为什么这个特性特别重要: 在传参给函数时,如果传入的是可变类型,函数内部的修改会影响到外部的变量;而传入不可变类型,函数内部无论如何修改都不会影响外部。这个知识点在面试中几乎是必问的。

1.3、Python 数据类型总表

下面列出 Python 中最常用的数据类型:

类型 类别 说明 示例
int 不可变 整数 42, -7, 0
float 不可变 浮点数(小数) 3.14, -0.5, 1e5
str 不可变 字符串(文本) "hello", '你好'
bool 不可变 布尔值(真假) True, False
list 可变 列表(有序,可重复) [1, 2, 3], ["a", "b"]
dict 可变 字典(键值对) {"name": "Alice", "age": 30}
tuple 不可变 元组(有序,可重复) (1, 2, 3), ("a", "b")
set 可变 集合(无序,不重复) {1, 2, 3}, {"apple", "banana"}

2、整数(int):不只是 1、2、3

2.1、整数类型的特点

Python 的 int 类型是任意精度整数(Arbitrary-Precision Integer),这是 Python 3 相对于其他语言(如 C、Java)的一个巨大优势。

具体来说:

  • Python 的整数没有大小限制,只要内存够大,你可以表示任意大的数字
  • 不像 C/Java 有 int(32位,最多表示约21亿)、long(64位)、short(16位)的区分
  • Python 3 中 int 就是长整型,而且自动扩容,你不需要手动选择类型
python 复制代码
# ========== Python 的整数没有大小限制 ==========
print("===== 任意精度整数 =====")

# 计算 2 的 10000 次方
huge = 2 ** 10000  # ** 是幂运算符,表示 2 的 10000 次方
print(f"2 的 10000 次方有 {len(str(huge))} 位数字")
# len(str(huge)) 是把数字转成字符串,再求长度
# 输出:2 的 10000 次方有 3011 位数字

# 对比其他语言:
# Java int 上限:2^31 - 1 = 2147483647 ≈ 21 亿
# C long 在 64 位系统上:2^63 - 1 ≈ 922 亿亿
# 而 Python 可以轻松表示 2^1000000(100万次方),只是输出会很长

# 再来一个例子:100!(100的阶乘)
import math
factorial_100 = math.factorial(100)
print(f"100! 有 {len(str(factorial_100))} 位")
# 输出:100! 有 158 位

# Python 可以直接运算,不需要担心溢出
big_num = 9999999999999999999999999999999999999999 + 1
print(big_num)
# 输出:10000000000000000000000000000000000000000

2.2、整数的四种进制表示

Python 支持四种进制的表示方法,这在系统编程、嵌入式、通信协议、网络等领域非常有用。

什么是进制?

  • 十进制(Decimal):我们平时使用的计数法,0-9,逢十进一
  • 二进制(Binary):计算机内部使用的计数法,0-1,逢二进一
  • 八进制(Octal):0-7,逢八进一,有时用于 Unix 文件权限
  • 十六进制(Hexadecimal):0-9 + A-F,逢十六进一,常用于内存地址、颜色代码
python 复制代码
# ========== 四种进制表示 ==========
print("===== 四种进制 =====")

a = 42        # 十进制(默认)------ 人类习惯的写法
b = 0b101010 # 二进制,以 0b 开头(b = binary)
c = 0o52     # 八进制,以 0o 开头(o = octal)
d = 0x2a     # 十六进制,以 0x 开头(x = hex)

print(f"十进制 42 = {a}")
print(f"二进制 0b101010 = {b}")
print(f"八进制 0o52 = {c}")
print(f"十六进制 0x2a = {d}")
# 全部相等!只是表示形式不同:
# 输出:
# 十进制 42 = 42
# 二进制 0b101010 = 42
# 八进制 0o52 = 42
# 十六进制 0x2a = 42

进制之间的互相转换:

python 复制代码
# ========== 进制转换函数 ==========
print("===== 进制转换 =====")

num = 42

# 十进制 → 其他进制(得到字符串)
print(bin(num))   # '0b101010'  二进制字符串
print(oct(num))   # '0o52'     八进制字符串
print(hex(num))   # '0x2a'     十六进制字符串

# 其他进制字符串 → 十进制整数
print(int('101010', 2))   # 42,按二进制解析字符串 '101010'
print(int('0b101010', 2)) # 42,也可以带上前缀
print(int('0x2a', 16))    # 42,按十六进制解析字符串 '0x2a'

# 实用的 IP 地址转换例子
ip = "192.168.1.1"
parts = ip.split('.')
print(f"IP {ip} 的四个部分:{parts}")
# 输出:IP 192.168.1.1 的四个部分:['192', '168', '1', '1']

进制的应用场景:

  • 二进制:网络编程、位运算、硬件控制
  • 八进制 :Unix/Linux 文件权限(chmod 755
  • 十六进制 :内存地址(0x7fff5fbff8a0)、颜色代码(#FF5733)、调试信息

2.3、整数运算

python 复制代码
# ========== 整数基本运算 ==========
print("===== 整数运算 =====")

a, b = 17, 5

print(f"{a} + {b} = {a + b}")      # 22  加法
print(f"{a} - {b} = {a - b}")      # 12  减法
print(f"{a} * {b} = {a * b}")      # 85  乘法
print(f"{a} / {b} = {a / b}")      # 3.4 除法(注意:结果永远是浮点数!)
print(f"{a} // {b} = {a // b}")    # 3   整除(向下取整)
print(f"{a} % {b} = {a % b}")      # 2   取余(模运算)
print(f"{a} ** {b} = {a ** b}")    # 1419857  幂运算(17的5次方)
print(f"-{a} = {-a}")              # -17 负号

整除的坑------向负无穷取整:

这是 Python 新手最容易踩的坑之一:

python 复制代码
# ========== 整除的坑 ==========
print("===== 整除的坑 =====")

# 在 Python 中,整除 // 是向负无穷取整,不是向零取整!
print(f"-7 // 2 = {-7 // 2}")    # -4(不是 -3!)
print(f"-7 % 2 = {-7 % 2}")      # 1(不是 -1!)

print(f"7 // -2 = {7 // -2}")    # -4(不是 -3!)
print(f"7 % -2 = {7 % -2}")      # -1(不是 1!)

# 这背后的原理是什么?
# Python 的整除满足一个数学恒等式:a = (a//b) * b + a%b
# 也就是说:a除以b的商 * b + 余数 = a

# 验证 -7 的情况:
# -7 = (-4) * 2 + 1  →  -8 + 1 = -7 ✅ 正确!
# 如果按向零取整:-7 = (-3) * 2 + (-1)  →  -6 - 1 = -7 ✅ 数学上也对
# 但 Python 选择了向负无穷取整,这是 IEEE 754 标准中关于除法取整的惯例

# 如果你需要向零取整(即 C/Java 的行为),可以这样做:
import math
print(f"向零取整:{math.trunc(-7 / 2)}")  # -3(截断小数部分)

# 理解正数的整除没有坑:
print(f"7 // 2 = {7 // 2}")    # 3(向下取整,也是向零取整,结果一样)
print(f"7 % 2 = {7 % 2}")      # 1

练习: 自己动手运行上面的代码,感受整除在正数和负数上的不同行为。

3、浮点数(float):精度陷阱与应对

3.1、浮点数的表示

Python 的 float 类型对应 IEEE 754 双精度浮点数(64位),与 C 中的 double 相同。简单来说,float 就是 "小数",可以用来表示 3.14、-0.001、1.5e10(科学计数法)等。

python 复制代码
# ========== 浮点数基础 ==========
print("===== 浮点数基础 =====")

a = 3.14           # 普通小数
b = 1.0e-5         # 科学计数法:1.0 × 10^-5 = 0.00001
c = 1e10           # 科学计数法:1.0 × 10^10 = 10000000000.0
d = -2.5           # 负浮点数

print(f"a = {a}, 类型 = {type(a)}")
print(f"b = {b}(1.0 × 10^-5)")
print(f"c = {c}(1.0 × 10^10)")
print(f"d = {d}")
print(f"全部都是 float 类型:{type(b)}, {type(c)}, {type(d)}")

# 查看浮点数的精度信息
import sys
print(f"\n浮点数精度信息:{sys.float_info}")
# 输出类似:
# sys.float_info(max=1.7976931348623157e+308, max_exp=1024, ...)

3.2、精度陷阱:0.1 + 0.2 != 0.3

这是 Python(实际上是所有 IEEE 754 浮点数语言)最著名的精度问题之一。这不是 Python 的 bug,而是 IEEE 754 浮点数标准的固有特性

python 复制代码
# ========== 著名的 0.1 + 0.2 问题 ==========
print("===== 浮点数精度陷阱 =====")

result = 0.1 + 0.2
print(f"0.1 + 0.2 = {result}")
print(f"result == 0.3: {result == 0.3}")
# 输出:
# 0.1 + 0.2 = 0.30000000000000004
# result == 0.3: False

# 为什么会这样?
# 因为 0.1 和 0.2 在二进制(计算机内部)下是无限循环小数:
# 0.1(十进制) = 0.0001100110011001100110011...(二进制,无限循环)
# 0.2(十进制) = 0.001100110011001100110011...(二进制,无限循环)
# 计算机只能存储有限位(64位),所以必须截断,产生舍入误差
# 这个微小的误差在加法后被放大,导致 0.1 + 0.2 不完全等于 0.3

# 让我们看看 0.1 的精确二进制表示:
print(f"0.1 的 repr:{repr(0.1)}")  # 0.1(实际存储的是近似值)

如何正确比较浮点数? 有三种方法:

python 复制代码
# ========== 正确比较浮点数 ==========
print("===== 正确比较浮点数 =====")

import math
from decimal import Decimal

a = 0.1 + 0.2
b = 0.3

# 方法1:math.isclose()(最推荐)
print(f"math.isclose(a, b): {math.isclose(a, b)}")  # True

# 方法2:设置绝对容差
tolerance = 1e-9
print(f"abs(a - b) < 1e-9: {abs(a - b) < tolerance}")  # True

# 方法3:使用 Decimal 精确运算(最精确,用于金融计算)
a_dec = Decimal('0.1')  # 注意:要用字符串 '0.1',不要用 float 0.1
b_dec = Decimal('0.2')
result_dec = a_dec + b_dec
print(f"Decimal 精确计算:{a_dec} + {b_dec} = {result_dec}")
print(f"Decimal('0.3') == result_dec: {Decimal('0.3') == result_dec}")  # True

⚠️ 踩坑提醒:绝对不能用 float 做金钱计算!

python 复制代码
# ========== 金钱计算的大坑 ==========
print("===== 金钱计算不能用 float =====")

# 场景:计算 0.1 + 0.2 + 0.3 - 0.6
result = 0.1 + 0.2 + 0.3 - 0.6
print(f"0.1 + 0.2 + 0.3 - 0.6 = {result}")  # 5.551115123125783e-17(接近0但不是0)

# 场景:电商计算价格
price1 = 0.1
price2 = 0.2
total = price1 + price2
print(f"0.1 + 0.2 = {total}")
if total == 0.3:
    print("正好等于0.3")
else:
    print(f"不等于0.3!差了 {total - 0.3}")

# 正确的做法:用 Decimal 或用整数(以"分"为单位)
print("\n正确的金钱计算方式:")
from decimal import Decimal
price1 = Decimal('0.10')
price2 = Decimal('0.20')
print(f"Decimal 精确计算:{price1} + {price2} = {price1 + price2}")

# 或者用整数(以"分"为单位)
price1_cent = 10  # 0.10 元 = 10 分
price2_cent = 20  # 0.20 元 = 20 分
total_cent = price1_cent + price2_cent
print(f"整数(分)计算:{price1_cent}分 + {price2_cent}分 = {total_cent}分 = {total_cent/100}元")

3.3、浮点数与整数的转换

python 复制代码
# ========== 类型转换 ==========
print("===== 浮点数与整数转换 =====")

# 浮点数 → 整数(直接截断小数部分,不是四舍五入!)
print(f"int(3.99) = {int(3.99)}")     # 3(不是 4!直接砍掉小数)
print(f"int(3.14) = {int(3.14)}")     # 3
print(f"int(-3.99) = {int(-3.99)}")   # -3(也是截断,不是向零取整)

# 如果需要四舍五入,用 round()
print(f"round(3.5) = {round(3.5)}")   # 4(四舍五入)
print(f"round(3.4) = {round(3.4)}")   # 3
print(f"round(3.14159, 2) = {round(3.14159, 2)}")  # 3.14,保留两位小数
print(f"round(3.14159, 4) = {round(3.14159, 4)}")  # 3.1416,保留四位小数

# 整数 → 浮点数(自动类型提升)
print(f"float(3) = {float(3)}")       # 3.0
print(f"float(0) = {float(0)}")       # 0.0

# 浮点数和整数比较
print(f"\n1 == 1.0: {1 == 1.0}")      # True(值相等)
print(f"1 is 1.0: {1 is 1.0}")        # False(身份不同,类型不同)

4、字符串(str):不只是字符数组

相关推荐
fīɡЙtīиɡ ℡2 小时前
【SpringAi最新版入门(二)】
java·javascript·css·人工智能·css3
AI科技星2 小时前
张祥前统一场论中两个电荷定义的统一性解析
开发语言·线性代数·算法·数学建模·平面
代码地平线2 小时前
C语言实现堆与堆排序详解:从零手写到TopK算法及时间复杂度证明
c语言·开发语言·算法
小江的记录本2 小时前
【大语言模型】大语言模型——核心概念(预训练、SFT监督微调、RLHF/RLAIF对齐、Token、Embedding、上下文窗口)
java·人工智能·后端·python·算法·语言模型·自然语言处理
西西学代码2 小时前
查找设备页面(amap_map)
开发语言·前端·javascript
坐吃山猪2 小时前
Python04_序列和字符串
python
念越2 小时前
算法每日一题 Day01|双指针解决移动零问题
java·算法·力扣
敖正炀2 小时前
StampedLock 详解
java·后端
AllData公司负责人2 小时前
AllData数据中台集成开源项目Apache Doris建设实时数仓平台
java·大数据·数据库·数据仓库·apache doris·实时数仓平台·doris集群