基础类型与类型语义
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 判断 |
| 布尔 | bool | True、False;是 int 的子类 |
| 整数 | int | 任意精度,数值增大时内存与计算成本也会增加 |
| 浮点数 | float | 通常为 IEEE 754 双精度,不能精确表示多数十进制小数 |
| 复数 | complex | 由实部和虚部组成,如 1 + 2j |
| 文本 | str | 不可变 Unicode 文本 |
| 二进制 | bytes、bytearray | 字节序列;前者不可变,后者可变 |
| 序列 | list、tuple、range | 列表可变;元组和区间不可变 |
| 映射 | dict | 可变键值映射,保持插入顺序 |
| 集合 | set、frozenset | 元素唯一;前者可变,后者不可变 |
数值类型的细节
bool 也是整数
bool 继承自 int,所以布尔值可以参与算术,而且与 0、1 相等。
isinstance(True, int) # True
True + True # 2
True == 1 # True这也意味着 True 和 1 不能作为两个不同的字典键:
values = {True: "yes", 1: "one"}
print(values) # {True: 'one'}业务数据中若要严格区分布尔值与整数,需要先检查精确类型。
除法、整除与取模
5 / 2 # 2.5,真除法总是产生 float
5 // 2 # 2,向负无穷取整
-5 // 2 # -3
-5 % 2 # 1,满足 a == (a // b) * b + a % b// 不是简单地截掉小数部分,因此负数场景尤其容易写错。
浮点数不是十进制小数
0.1 + 0.2 == 0.3 # False比较计算结果应使用容差;金额等需要精确十进制语义的数据使用 Decimal,并从字符串构造。
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 表示原始字节。二者不能隐式拼接,必须明确编码和解码。
text = "你好"
data = text.encode("utf-8")
len(text) # 2,字符数量
len(data) # 6,字节数量
data.decode("utf-8") # '你好'
text[0] # '你'
data[0] # 228,bytes 索引返回整数str 和 bytes 都不可变。需要原地修改二进制内容时使用 bytearray:
buffer = bytearray(b"abc")
buffer[0] = ord("A")
bytes(buffer) # b'Abc'循环中拼接大量字符串通常用 "".join(parts),避免反复创建中间字符串。
None 与缺失值
None 表示“没有值”,判断时使用身份比较:
if result is None:
...当 None 本身也是合法输入时,需要单独的哨兵对象区分“未传入”和“显式传入 None”。
_MISSING = object()
def configure(value=_MISSING):
if value is _MISSING:
return "use default"
return value可变性与可哈希性
| 类型 | 可变 | 通常可作为 dict 键或 set 元素 |
|---|---|---|
int、float、bool、NoneType | 否 | 是 |
str、bytes | 否 | 是 |
tuple | 否 | 所有元素都可哈希时才可以 |
frozenset | 否 | 是 |
list、dict、set、bytearray | 是 | 否 |
不可变不等于一定可哈希。元组自身不可变,但其中若包含列表,整个元组仍不可哈希。
hash((1, "a")) # 可以
hash((1, [])) # TypeError容器是否可变描述的是容器自身。元组不能替换元素,但它引用的可变对象仍然可以改变:
item = (1, [2, 3])
item[1].append(4)
print(item) # (1, [2, 3, 4])真值判断
以下对象在布尔上下文中为假:
None、False- 数值零,如
0、0.0、0j - 空字符串和空容器,如
""、[]、{}、set() - 自定义类型中
__bool__()返回False,或没有__bool__()且__len__()返回0的对象
if items:
print("非空")若 0、空字符串或空容器是有效值,不要用真值判断代替缺失判断:
# 错误:0 也会被当作未提供
timeout = user_timeout or 30
# 明确区分 None
timeout = 30 if user_timeout is None else user_timeout类型转换的常见陷阱
构造器通常按“可迭代对象”工作,不一定按人对文本的直觉工作。
list("abc") # ['a', 'b', 'c']
tuple({"a": 1}) # ('a',),迭代 dict 得到键
set("banana") # {'b', 'a', 'n'},顺序不保证
int("ff", 16) # 255dict.fromkeys() 会让所有键引用同一个值。可变默认值应通过推导式分别创建:
wrong = dict.fromkeys(["a", "b"], [])
wrong["a"].append(1)
print(wrong) # {'a': [1], 'b': [1]}
right = {key: [] for key in ("a", "b")}类型注解不会自动检查运行时值
def add(x: int, y: int) -> int:
return x + y
add("a", "b") # 运行时仍会返回 'ab'类型注解主要供阅读、IDE 和静态类型检查器使用。若外部输入需要运行时校验,仍应显式转换和验证。