主题
基本类型
计算机从诞生之初就被设计为"计算的机器",但这个名字其实有些误导——它不仅能处理数值,还能驾驭文本、图像、声音乃至更复杂的信息形态。就像人类用语言描述世界,计算机也需要一套"语法"来区分不同类型的数据:数字该如何存储?文字怎样编码?真假判断如何表示?这些问题的答案,构成了编程语言的基石——基本类型。
在大模型开发中,我们打交道最多的数据是张量(由数字组成)和字符串(文本),所以对这些基本类型的深入理解非常重要。尤其是浮点数的精度问题和类型转换的陷阱,在数据处理中经常会导致难以发现的 bug,我们首先介绍的是张量:整数、浮点数、布尔值这几种。
变量和对象
在深入探讨基本类型之前,我们需要先澄清两个核心概念:变量和对象。
对象是内存中存储数据的实体。Python 中的一切都是对象——整数是对象,字符串是对象,函数也是对象。每个对象都有三个基本属性:身份(identity,即内存地址)、类型(type)和值(value)。你可以用 id() 查看对象的身份,用 type() 查看对象的类型。
变量则是指向对象的标签,或者说"名字"。变量本身不存储数据,它只是绑定到某个对象上的引用。想象一个场景:a = 5,这里整数 5 是一个对象,存储在内存的某个位置;a 是我们给这个对象起的名字。如果接着写 b = a,并不是复制了 5 这个值,而是让 b 也指向同一个对象。
在后续的章节中,我们还会继续探讨。
整数:精确的整型运算
整数是 Python 中最常用的数据类型之一。大模型处理的所有数据最终都是数字,整数运算是最基础的。
python
x = 5
y = 3
print(x + y) # 加法:8
print(x - y) # 减法:2
print(x * y) # 乘法:15
print(x / y) # 除法:1.6666666666666667
print(x // y) # 整除:1
print(x % y) # 取余:2
print(x ** y) # 幂运算:125整数在 Python 中是不可变对象。当你对整数执行任何"修改"操作时,实际上是创建了一个新的整数对象。这个机制初看可能觉得低效,但它的设计意图是保证整数对象的安全共享——如果两个变量都指向整数 100,它们其实指向同一个对象,通过一个变量修改它会让另一个变量也受影响,后果不堪设想。不可变性让这种情况永远不会发生。
Python 对小整数有缓存机制。当你在代码中写出 a = 100 这样的字面量时,Python 可能会直接复用内部缓存的整数对象,而不是每次都创建新的。这是为了优化性能和内存。但不要依赖这个行为,因为它只对特定范围的整数生效。
浮点数:精度有限的数字
浮点数用来表示带小数部分的数字:
python
price = 19.99
ratio = 0.75
temperature = -5.5浮点数支持和整数一样的算术运算,但有一个著名的陷阱:
python
result = 0.1 + 0.2
print(result) # 0.30000000000000004这不是 Python 的 bug,而是浮点数在计算机中的表示方式的固有限制。浮点数采用 IEEE 754 标准,用二进制表示十进制小数。但许多十进制小数无法精确用二进制表示,只能无限接近。所以 0.1 + 0.2 的结果不是精确的 0.3,而是一个接近 0.3 的二进制近似值。
这个精度问题在涉及金钱计算时尤其危险:
python
total = 0.0
for i in range(10):
total += 0.1
print(total) # 0.9999999999999999如果用这个结果做条件判断,可能会出现意想不到的行为。正确的做法是使用 decimal 模块:
python
from decimal import Decimal
total = Decimal('0.0')
for i in range(10):
total += Decimal('0.1')
print(total) # 1.0注意这里用的是字符串 '0.1' 而不是浮点数 0.1,否则精度问题在计算前就已经存在。不过在大模型开发的场景中,这种精度误差通常不会影响核心逻辑,因为我们处理的主要是向量和概率,而不是精确的金融计算。
布尔值:真与假
布尔值只有两个对象:True 和 False:
python
is_active = True
has_permission = False布尔值在 Python 中不仅仅是条件判断的工具,它们还参与更底层的逻辑运算。and 和 or 运算符返回的不是布尔值,而是参与运算的对象本身:
python
a = "" or "default"
print(a) # default
b = "hello" and "world"
print(b) # worldor 的规则是:如果左操作数为真,就返回左操作数,否则返回右操作数。and 则相反,如果左操作数为假,就返回左操作数,否则返回右操作数。
布尔值和整数可以相互转换,True 在数值上等于 1,False 等于 0:
python
print(True + True) # 2
print(True + False) # 1虽然可以这样用,但在实际代码中建议保持布尔值的语义清晰,不要用它来做算术运算。
类型转换:显式与隐式
Python 是强类型语言,不同类型的对象不能随意混用,必须显式转换。
最常见的类型转换是字符串和数字之间的转换:
python
# 字符串转数字
age = int("25")
price = float("19.99")
# 数字转字符串
age_str = str(25)
price_str = str(19.99)
# 浮点数转整数(截断小数部分)
pi = int(3.14159) # 3,不是四舍五入类型转换中有一个容易踩的坑:
python
# 这个会报错
number = int("3.14")
# 正确做法是先转浮点数再转整数
number = int(float("3.14")) # 3隐式类型转换发生在运算过程中。比如整数和浮点数相加时,Python 会自动把整数转换成浮点数:
python
result = 3 + 0.5 # Python 自动把 3 转换成 3.0,结果是 3.5这种隐式转换虽然方便,但也可能导致精度问题或者类型不一致的问题。在工程代码中,建议保持类型的一致性,不要依赖隐式转换。
基本类型的共同特征
整数、浮点数、布尔值有一个共同点:都是不可变对象。这意味着它们创建之后,值就不能改变。这个设计选择背后有深刻的考量。
首先,不可变对象是线程安全的。多个变量可以安全地共享同一个不可变对象,不需要任何同步机制。其次,不可变对象可以作为字典的键和集合的元素,因为它们的哈希值是固定的。最后,不可变对象利于缓存和内存优化,Python 可以在内部缓存常见的不可变对象,避免重复创建。
理解基本类型是理解更复杂数据结构的基础。当我们谈论变量、对象、引用这些概念时,基本类型是最简单的例子。掌握了基本类型的不可变性,再去看列表、字典这些可变对象,就能更清楚地理解它们之间的区别。