Skip to content

基础类型与类型语义

Python 中变量没有固定类型,名字只是对对象的引用;类型属于对象本身。

python
x = 1
print(type(x))          # <class 'int'>

x = "one"
print(type(x))          # <class 'str'>
print(isinstance(x, str))  # True

判断对象类型通常使用 isinstance(),它会考虑继承关系;只有需要判断“必须是这个精确类型”时才使用 type(x) is T

内置基础类型

分类类型关键特征
空值NoneType只有一个值 None,用 is None 判断
布尔boolTrueFalse;是 int 的子类
整数int任意精度,数值增大时内存与计算成本也会增加
浮点数float通常为 IEEE 754 双精度,不能精确表示多数十进制小数
复数complex由实部和虚部组成,如 1 + 2j
文本str不可变 Unicode 文本
二进制bytesbytearray字节序列;前者不可变,后者可变
序列listtuplerange列表可变;元组和区间不可变
映射dict可变键值映射,保持插入顺序
集合setfrozenset元素唯一;前者可变,后者不可变

数值类型的细节

bool 也是整数

bool 继承自 int,所以布尔值可以参与算术,而且与 01 相等。

python
isinstance(True, int)  # True
True + True            # 2
True == 1              # True

这也意味着 True1 不能作为两个不同的字典键:

python
values = {True: "yes", 1: "one"}
print(values)  # {True: 'one'}

业务数据中若要严格区分布尔值与整数,需要先检查精确类型。

除法、整除与取模

python
5 / 2    # 2.5,真除法总是产生 float
5 // 2   # 2,向负无穷取整
-5 // 2  # -3
-5 % 2   # 1,满足 a == (a // b) * b + a % b

// 不是简单地截掉小数部分,因此负数场景尤其容易写错。

浮点数不是十进制小数

python
0.1 + 0.2 == 0.3  # False

比较计算结果应使用容差;金额等需要精确十进制语义的数据使用 Decimal,并从字符串构造。

python
import math
from decimal import Decimal
from fractions import Fraction

math.isclose(0.1 + 0.2, 0.3)
Decimal("0.1") + Decimal("0.2") == Decimal("0.3")
Fraction(1, 3) + Fraction(1, 6) == Fraction(1, 2)

float("nan") 与任何值都不相等,包括它自己,应通过 math.isnan() 判断。

str、bytes 与 bytearray

str 表示文本,bytes 表示原始字节。二者不能隐式拼接,必须明确编码和解码。

python
text = "你好"
data = text.encode("utf-8")

len(text)              # 2,字符数量
len(data)              # 6,字节数量
data.decode("utf-8")  # '你好'

text[0]  # '你'
data[0]  # 228,bytes 索引返回整数

strbytes 都不可变。需要原地修改二进制内容时使用 bytearray

python
buffer = bytearray(b"abc")
buffer[0] = ord("A")
bytes(buffer)  # b'Abc'

循环中拼接大量字符串通常用 "".join(parts),避免反复创建中间字符串。

None 与缺失值

None 表示“没有值”,判断时使用身份比较:

python
if result is None:
    ...

None 本身也是合法输入时,需要单独的哨兵对象区分“未传入”和“显式传入 None”。

python
_MISSING = object()

def configure(value=_MISSING):
    if value is _MISSING:
        return "use default"
    return value

可变性与可哈希性

类型可变通常可作为 dict 键或 set 元素
intfloatboolNoneType
strbytes
tuple所有元素都可哈希时才可以
frozenset
listdictsetbytearray

不可变不等于一定可哈希。元组自身不可变,但其中若包含列表,整个元组仍不可哈希。

python
hash((1, "a"))  # 可以
hash((1, []))   # TypeError

容器是否可变描述的是容器自身。元组不能替换元素,但它引用的可变对象仍然可以改变:

python
item = (1, [2, 3])
item[1].append(4)
print(item)  # (1, [2, 3, 4])

真值判断

以下对象在布尔上下文中为假:

  • NoneFalse
  • 数值零,如 00.00j
  • 空字符串和空容器,如 ""[]{}set()
  • 自定义类型中 __bool__() 返回 False,或没有 __bool__()__len__() 返回 0 的对象
python
if items:
    print("非空")

0、空字符串或空容器是有效值,不要用真值判断代替缺失判断:

python
# 错误:0 也会被当作未提供
timeout = user_timeout or 30

# 明确区分 None
timeout = 30 if user_timeout is None else user_timeout

类型转换的常见陷阱

构造器通常按“可迭代对象”工作,不一定按人对文本的直觉工作。

python
list("abc")          # ['a', 'b', 'c']
tuple({"a": 1})     # ('a',),迭代 dict 得到键
set("banana")       # {'b', 'a', 'n'},顺序不保证
int("ff", 16)       # 255

dict.fromkeys() 会让所有键引用同一个值。可变默认值应通过推导式分别创建:

python
wrong = dict.fromkeys(["a", "b"], [])
wrong["a"].append(1)
print(wrong)  # {'a': [1], 'b': [1]}

right = {key: [] for key in ("a", "b")}

类型注解不会自动检查运行时值

python
def add(x: int, y: int) -> int:
    return x + y

add("a", "b")  # 运行时仍会返回 'ab'

类型注解主要供阅读、IDE 和静态类型检查器使用。若外部输入需要运行时校验,仍应显式转换和验证。