Skip to content

01-变量与数据类型

变量是程序存储数据的基本方式,理解变量和数据类型是编程的第一步。


本章讲解变量的概念、命名规则和 Python 基本数据类型。


概念铺垫

为什么需要变量?一个真实的存储场景

问题场景: 你在开发一个学生信息管理程序,需要存储每个学生的姓名、年龄、成绩。

不使用变量的困惑:

  • 如何在程序中"记住"这些数据?
  • 数据处理完后还能找到吗?
  • 如何在程序的不同地方使用相同的数据?

使用变量的解决方案:

python
# 用变量存储学生信息
name = "张三"
age = 18
score = 85.5

# 在程序中多次使用
print(f"学生姓名:{name}")  # 输出:张三
print(f"年龄:{age}岁")     # 输出:18岁
print(f"成绩:{score}分")   # 输出:85.5分

# 数据仍然保留,可以继续使用
average = score / 3  # 计算平均分

这就是变量的价值:用一个名字,存储和复用数据


变量解决了什么问题?

变量的本质是:给数据一个"名字",方便存储和引用

就像你给文件贴标签,方便以后找到。变量就是程序中的"标签"。

变量的优势:

  1. 存储数据:程序运行时保存信息
  2. 复用数据:同一个数据多次使用,不用重复写
  3. 修改数据:可以随时更新存储的内容
  4. 传递数据:在不同地方传递和处理数据

数据类型的作用:

数据类型告诉程序"这个变量是什么",就像标签上的说明:

  • int:整数(如年龄:18)
  • float:浮点数(如成绩:85.5)
  • str:字符串(如姓名:"张三")
  • bool:布尔值(如是否通过:True)

变量的最简用法

变量只需要3步:创建、使用、修改。

python
# 创建变量(变量名 = 值)
name = "Python"
age = 33

# 使用变量
print(name)  # 输出:Python
print(age)   # 输出:33

# 修改变量
age = 34
print(age)   # 输出:34

这就是变量的基本用法。接下来我们详细学习变量的特性。


什么是变量

变量(Variable) 是程序中用于存储数据的"容器"。

┌─────────────────────────────────────────┐
│              变量示意图                  │
├─────────────────────────────────────────┤
│                                         │
│    变量名        变量值                  │
│      │            │                     │
│      ▼            ▼                     │
│   ┌─────┐     ┌───────┐                 │
│   │ name│────▶│"Alice"│                 │
│   └─────┘     └───────┘                 │
│                                         │
│   ┌─────┐     ┌───────┐                 │
│   │ age │────▶│  25   │                 │
│   └─────┘     └───────┘                 │
│                                         │
└─────────────────────────────────────────┘

L1 理解层:会用

变量的创建和使用

基本语法

┌─────────────────────────────────────────┐
│            变量赋值语法                  │
├─────────────────────────────────────────┤
│                                         │
│   变量名 = 值                            │
│                                         │
│   读作:"将 值 赋给 变量名"              │
│                                         │
└─────────────────────────────────────────┘

示例代码

python
# 创建变量
name = "Alice"
age = 25
height = 1.68
is_student = True

# 使用变量
print(name)        # 输出:Alice
print(age)         # 输出:25
print(height)      # 输出:1.68
print(is_student)  # 输出:True

变量命名规则

┌─────────────────────────────────────────┐
│          变量命名规则                    │
├─────────────────────────────────────────┤
│                                         │
│  ✅ 可以做:                             │
│  • 只能包含字母、数字、下划线 (_)        │
│  • 可以以字母或下划线开头               │
│  • 区分大小写(name 和 Name 不同)        │
│                                         │
│  ❌ 不可以:                             │
│  • 不能以数字开头                        │
│  • 不能包含空格或特殊字符               │
│  • 不能使用 Python 关键字               │
│                                         │
└─────────────────────────────────────────┘
python
# ✅ 正确的命名
name = "Alice"
user_name = "Bob"
_age = 25

# ❌ 错误的命名
# 2name = "David"    # 不能以数字开头
# user-name = "Eve"  # 不能有连字符
# class = "Math"     # 不能使用关键字

变量的赋值方式

链式赋值

python
# 多个变量赋相同的值
a = b = c = 0
print(a, b, c)  # 输出:0 0 0

多重赋值

python
# 同时给多个变量赋不同的值
name, age, city = "Alice", 25, "Beijing"
print(name)  # 输出:Alice
print(age)   # 输出:25
print(city)  # 输出:Beijing

交换变量值

Python 特性: 无需临时变量,直接交换。

交换变量值:
┌─────────────────────────────────────────────────────────────┐
│  Python 变量交换                                              │
│                                                              │
│  a, b = b, a                                                 │
│  ↑    ↑                                                      │
│  赋值  元组解包                                               │
│                                                              │
│  执行过程:                                                   │
│  1. 右侧 b, a 先打包为元组 (b, a)                            │
│  2. 元组解包赋值给左侧 a, b                                   │
│                                                              │
│  示例:                                                       │
│  a = 10, b = 20                                              │
│  a, b = b, a                                                 │
│  结果:a = 20, b = 10                                         │
└─────────────────────────────────────────────────────────────┘

最简示例

python
a = 10
b = 20
a, b = b, a  # 一行交换
print(a, b)  # 输出:20 10

关键代码解释

代码含义执行顺序
b, a右侧表达式先打包为元组 (20, 10)
a, b =左侧赋值元组解包赋值

数据类型

Python 常见数据类型

┌─────────────────────────────────────────┐
│          Python 常见数据类型             │
├─────────────────────────────────────────┤
│                                         │
│  数值类型:                             │
│  • int(整数):1, 2, 100, -5          │
│  • float(浮点数):3.14, -0.5         │
│                                         │
│  文本类型:                             │
│  • str(字符串):"Hello", "Python"    │
│                                         │
│  布尔类型:                             │
│  • bool(布尔):True, False           │
│                                         │
│  空值类型:                             │
│  • NoneType(空值):None              │
│                                         │
└─────────────────────────────────────────┘

查看数据类型

python
# 使用 type() 函数查看类型
name = "Alice"
age = 25
height = 1.68

print(type(name))   # 输出:<class 'str'>
print(type(age))    # 输出:<class 'int'>
print(type(height)) # 输出:<class 'float'>

检查变量类型

方法一:使用 type()

python
age = 25

# 检查类型
if type(age) == int:
    print("age 是整数")

问题: type() 无法正确判断继承关系

python
class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()
print(type(dog) == Animal)  # False(错误!)

方法二:使用 isinstance()(推荐)

isinstance: 检查对象是否属于指定类型,支持继承关系。

isinstance vs type:
┌─────────────────────────────────────────────────────────────┐
│  type() vs isinstance()                                       │
│                                                              │
│  type(obj) == SomeClass                                       │
│  • 精确匹配类型                                              │
│  • 不考虑继承关系                                            │
│                                                              │
│  isinstance(obj, SomeClass)                                   │
│  • 检查类型及父类                                            │
│  • 支持继承关系                                              │
│  • 支持多类型判断                                            │
│                                                              │
│  ⚠️ 推荐:使用 isinstance()                                   │
└─────────────────────────────────────────────────────────────┘

最简示例

python
age = 25
if isinstance(age, int):
    print("age 是整数")

关键代码解释

代码含义说明
isinstance(x, int)检查类型x 是否为 int 类型
isinstance(x, (int, str))多类型检查x 是否为 int 或 str

详细示例

python
# 支持多个类型判断
value = "hello"
if isinstance(value, (int, str)):
    print("value 是整数或字符串")

python
class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()
print(isinstance(dog, Animal))  # True(正确!)

type() vs isinstance()

┌─────────────────────────────────────────────────────┐
│         type() vs isinstance() 对比                  │
├─────────────────────────────────────────────────────┤
│                                                     │
│  type()                      isinstance()           │
│  ───────                     ───────────            │
│  • 检查确切类型               • 检查类型及父类       │
│  • 不考虑继承                 • 考虑继承关系         │
│  • 返回类型对象               • 返回布尔值           │
│  • 适合精确匹配               • 更灵活、更安全       │
│                                                     │
│  使用场景:                   使用场景:             │
│  • 需要知道具体类型           • 类型检查(推荐)     │
│  • 不涉及继承                 • 判断对象类型         │
│                               • 支持多类型判断       │
│                                                     │
│  示例:                       示例:                 │
│  type(123) == int            isinstance(123, int)   │
│  type(123)                   isinstance(x, (A,B))   │
│                                                     │
│  ❌ 不推荐用于类型检查        ✅ 推荐用于类型检查    │
│                                                     │
└─────────────────────────────────────────────────────┘

实际应用示例

python
def process_data(data):
    """处理不同类型的数据"""
    if isinstance(data, str):
        return data.upper()
    elif isinstance(data, (int, float)):
        return data * 2
    elif isinstance(data, list):
        return sum(data)
    else:
        return str(data)

# 测试
print(process_data("hello"))    # HELLO
print(process_data(10))         # 20
print(process_data([1, 2, 3]))  # 6
print(process_data({"a": 1}))   # {'a': 1}

字符串类型(str)

字符串(str): 用于表示文本数据,用引号括起来。

┌──────────────────────────────────────┐
│  字符串创建方式                       │
│                                      │
│  'text'     → 单引号字符串           │
│  "text"     → 双引号字符串           │
│  """text""" → 多行字符串             │
│  r"text"    → raw 字符串(不转义)   │
│  f"Hi {x}"  → 格式化字符串          │
└──────────────────────────────────────┘

创建字符串

python
# 单引号或双引号
name1 = 'Alice'
name2 = "Alice"

# 包含引号的字符串
quote1 = 'He said, "Hello!"'
quote2 = "It's a beautiful day"

# 多行字符串
poem = """
静夜思
床前明月光,
疑是地上霜。
"""

转义字符

常见转义字符:
  \"  → 双引号
  \'  → 单引号
  \\  → 反斜杠
  \n  → 换行符
  \t  → 制表符(Tab)

整数类型(int)

整数(int): 没有小数点的数字,Python 的整数没有大小限制。

┌──────────────────────────────────────┐
│  整数字面量写法                       │
│                                      │
│  100       → 十进制(默认)          │
│  0b1010    → 二进制(0b 前缀)       │
│  0o12      → 八进制(0o 前缀)       │
│  0xA       → 十六进制(0x 前缀)     │
│  1_000_000 → 下划线分隔(更易读)    │
└──────────────────────────────────────┘
python
# 正整数、负整数、零
positive = 100
negative = -50
zero = 0

# 不同进制
binary = 0b1010      # 二进制
octal = 0o12         # 八进制
hexadecimal = 0xA    # 十六进制

浮点数类型(float)

浮点数(float): 带小数点的数字,用于表示实数。

┌──────────────────────────────────────┐
│  浮点数写法                           │
│                                      │
│  3.14      → 普通小数                │
│  -0.5      → 负数                    │
│  1.5e3     → 科学计数法(= 1500.0)  │
│  2.5e-2    → 科学计数法(= 0.025)   │
│                                      │
│  ⚠️ 注意:0.1 + 0.2 ≠ 0.3(精度误差)│
└──────────────────────────────────────┘
python
# 浮点数
pi = 3.14159
negative_float = -0.5

# 科学计数法
scientific = 1.5e3   # 1500.0
small = 2.5e-2       # 0.025

# 注意:浮点数计算可能有精度误差
result = 0.1 + 0.2
print(result)  # 输出:0.30000000000000004

布尔类型(bool)

布尔(bool): 只有两个值:True(真)和 False(假),是 int 的子类。

┌──────────────────────────────────────┐
│  布尔值规则                           │
│                                      │
│  True   假值(bool(x) == False):   │
│  False  → 0, 0.0, "", [], {}, None  │
│                                      │
│  True   真值(bool(x) == True):    │
│  True   → 其他所有非零、非空值       │
│                                      │
│  bool 是 int 子类:True==1, False==0 │
└──────────────────────────────────────┘
python
# 布尔类型只有两个值
is_true = True
is_false = False

# 比较运算的结果
print(5 > 3)    # True
print(5 < 3)    # False

# 真假值规则
print(bool(0))         # False
print(bool(""))        # False
print(bool(1))         # True
print(bool("Hello"))   # True

None 类型(空值)

什么是 None

None 是 Python 中表示"空"或"无"的特殊值,是 NoneType 类型的唯一值。

┌─────────────────────────────────────────┐
│              None 的含义                 │
├─────────────────────────────────────────┤
│                                         │
│  None ≠ 0          (0 是数字)         │
│  None ≠ ""         ("" 是空字符串)    │
│  None ≠ []         ([] 是空列表)      │
│  None ≠ False      (False 是布尔值)   │
│                                         │
│  None 表示:                             │
│  • "没有值"                              │
│  • "未知"                                │
│  • "不存在"                              │
│  • "未设置"                              │
│                                         │
└─────────────────────────────────────────┘

实际场景

场景1:函数没有返回值

python
def greet(name: str) -> None:
    """打招呼函数,没有返回值"""
    print(f"你好,{name}!")

result = greet("张三")  # 输出:你好,张三!
print(result)           # 输出:None

场景2:可选参数的默认值

python
def find_user(user_id: int) -> str | None:
    """查找用户,找不到返回 None"""
    users = {1: "张三", 2: "李四"}
    return users.get(user_id)  # 找不到返回 None

name1 = find_user(1)   # "张三"
name2 = find_user(99)  # None(用户不存在)

场景3:占位符

python
# 先定义变量,稍后赋值
config: dict[str, str] | None = None

# 后续加载配置
config = load_config()

判断 None 的正确方式

python
x = None

# ✅ 推荐方式:使用 is
if x is None:
    print("x 是 None")

# ❌ 不推荐:使用 ==
if x == None:  # 能工作,但不符合规范
    print("x 是 None")

提示:为什么判断 None 推荐用 is?这涉及到 Python 的对象身份机制,我们将在下一节详细讲解。

None 与其他"空值"的区别

python
# 不同的"空"值
empty_str = ""        # 空字符串
empty_list = []       # 空列表
empty_dict = {}       # 空字典
zero = 0              # 数字零
false_val = False     # 布尔假
none_val = None       # 空值

# 布尔值转换
print(bool(empty_str))    # False
print(bool(empty_list))   # False
print(bool(empty_dict))   # False
print(bool(zero))         # False
print(bool(false_val))    # False
print(bool(none_val))     # False

# 但它们互不相等
print(None == "")     # False
print(None == [])     # False
print(None == 0)      # False
print(None == False)  # False

类型提示中的 None

python
from typing import Optional

# 方式1:使用 Optional(Python 3.10 之前)
def find(id: int) -> Optional[str]:
    ...

# 方式2:使用 | None(Python 3.10+,推荐)
def find(id: int) -> str | None:
    ...

变量比较:值与身份

在 Python 中,比较两个变量有两种含义:内容是否一样 vs 是不是同一个对象

第一步:判断值是否相等 (==)

这是最直观的"等于"概念。

python
a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)  # True,内容一样

第二步:查看内存身份 (id())

虽然内容一样,但它们是内存里的同一个列表吗? 用 id() 函数查看对象的"身份证号"(内存地址)。

python
a = [1, 2, 3]
b = [1, 2, 3]

print(id(a))  # 例如:140123...
print(id(b))  # 例如:140124... (地址不同!)

结论:虽然 a == b,但它们是两个独立的对象

第三步:判断身份是否相同 (is)

is 用于判断两个变量是否引用了同一个内存对象(等价于 id(a) == id(b))。

python
a = [1, 2, 3]
b = [1, 2, 3]
c = a          # c 指向 a 的内存地址

print(a is b)  # False(虽然内容一样,但是不同的对象)
print(a is c)  # True(c 和 a 是同一个对象)

深入理解:小整数池缓存

Python 为了效率,缓存了小整数(-5 到 256)。对于这些数字,is 可能会返回 True。

python
x = 100
y = 100
print(x is y)  # True (在小整数池范围内,复用了同一个对象)

m = 300
n = 300
print(m is n)  # False (超出范围,通常是不同对象)
print(m == n)  # True (值依然相等)

is== 的核心区别总结

比较运算符含义比较依据常用场景
==值相等内容是否一样比较数字、字符串、列表内容
is身份相同id() 是否一样判断 None、判断单例对象

最佳实践:

  • 比较数据用 ==
  • 判断 Noneis None

id() 函数与对象引用(进阶)

id() 不仅用于比较身份,还能帮我们理解 Python 的引用机制

示例:修改引用对象的副作用

python
list1 = [1, 2, 3]
list2 = list1  # list2 指向了 list1 的内存地址

print(id(list1) == id(list2))  # True

# 修改 list2 会影响 list1 吗?
list2.append(4)
print(list1)  # [1, 2, 3, 4] —— 是的!因为它们指向同一个对象。

示例:创建副本

如果不想互相影响,需要创建新对象

python
list1 = [1, 2, 3]
list2 = list(list1)  # 创建新列表

print(list1 is list2)  # False(不同对象)
list2.append(4)
print(list1)           # [1, 2, 3] —— list1 不受影响

类型转换

常见转换函数

┌─────────────────────────────────────────┐
│           常见类型转换函数               │
├─────────────────────────────────────────┤
│                                         │
│  int(x)     →  将 x 转换为整数           │
│  float(x)   →  将 x 转换为浮点数         │
│  str(x)     →  将 x 转换为字符串         │
│  bool(x)    →  将 x 转换为布尔值         │
│                                         │
└─────────────────────────────────────────┘

示例

python
# 字符串转整数
num_str = "123"
num_int = int(num_str)
print(num_int)  # 输出:123

# 浮点数转整数(截断)
print(int(3.9))  # 输出:3

# 整数转字符串
num = 123
text = str(num)
print(text)  # 输出:"123"

从简单到复杂:变量的渐进应用

层级1:单个变量

python
# 最简单的变量
name: str = "Python"
print(name)

层级2:多个变量

python
# 多个相关变量
name: str = "张三"
age: int = 18
score: float = 85.5

print(f"{name}, {age}岁, {score}分")

层级3:变量交换

python
# Python 特有的变量交换
a: int = 10
b: int = 20
a, b = b, a  # 一行完成交换
print(a, b)  # 输出:20 10

层级4:动态变量

python
# 变量可以改变类型(Python 动态类型特性)
x: int | str = 10
x = "现在是字符串"
print(x)

层级5:变量与运算

python
# 变量参与计算
price: float = 99.9
quantity: int = 3
total: float = price * quantity
print(f"总价:{total:.2f}")

综合应用:学生信息管理示例

这个示例综合运用变量和数据类型:

python
# 学生信息管理(Python 3.11+)
from typing import Any

def main() -> None:
    # 存储多个学生信息
    students: list[dict[str, Any]] = [
        {"name": "张三", "age": 18, "scores": [85, 90, 78]},
        {"name": "李四", "age": 19, "scores": [92, 88, 95]},
        {"name": "王五", "age": 17, "scores": [78, 82, 88]}
    ]
    
    # 处理每个学生数据
    for student in students:
        name: str = student["name"]
        age: int = student["age"]
        scores: list[int] = student["scores"]
        
        # 计算平均分(变量运算)
        average: float = sum(scores) / len(scores)
        
        # 判断是否通过(布尔变量)
        passed: bool = average >= 60
        
        # 输出信息
        print(f"{name}({age}岁): 平均分 {average:.1f} - {'通过' if passed else '未通过'}")

if __name__ == "__main__":
    main()

这个示例展示了:

  • 不同类型变量的定义和使用
  • 变量之间的运算
  • 布尔变量的判断应用
  • 变量在函数中的传递
  • 现代类型提示语法

关键代码说明:

代码含义为什么这样写
list[dict[str, Any]]元素为字典的列表,字典值类型不限Any 表示值可以是 str、int、list 等任意类型
student["name"]通过键访问字典中的值比索引更直观,一眼就知道取的是什么字段
sum(scores) / len(scores)计算列表元素的平均值sum 求总和,len 求元素数量,相除得平均
average >= 60比较运算,结果为布尔值将判断结果存入变量,代码更清晰可复用
{'通过' if passed else '未通过'}f-string 内嵌三元表达式根据布尔变量动态切换输出文字

类型提示入门(Python 3.6+)

为什么需要类型提示?

问题场景: 看这段代码,你能一眼看出 name 应该是什么类型吗?

python
name = get_user_name()  # 是字符串?还是数字?

使用类型提示后:

python
name: str = get_user_name()  # 一眼就知道是字符串

类型提示的好处:

  1. 代码更易读:一眼就知道变量应该是什么类型
  2. 编辑器更智能:VS Code 会提供自动补全
  3. 提前发现错误:某些工具可以检查类型错误

基本语法

变量类型注解:

python
# 语法:变量名: 类型 = 值
name: str = "张三"
age: int = 18
height: float = 1.68
is_student: bool = True

函数类型注解:

python
# 参数和返回值都可以添加类型提示
def greet(name: str) -> str:
    return f"你好,{name}"

def add(a: int, b: int) -> int:
    return a + b

常见类型

基本类型:

类型说明示例
str字符串name: str = "Python"
int整数age: int = 18
float浮点数price: float = 99.9
bool布尔值is_valid: bool = True

容器类型(简单了解):

python
# 列表
scores: list[int] = [85, 90, 78]

# 字典
student: dict[str, int] = {"张三": 85, "李四": 92}

使用场景

什么时候使用类型提示?

推荐使用:

  • 函数的参数和返回值
  • 重要的变量定义
  • 公共模块的代码

可以不用:

  • 简单的临时变量
  • 快速测试代码
  • 学习练习时

示例:

python
# ✅ 好的做法:函数添加类型提示
def calculate_average(scores: list[int]) -> float:
    return sum(scores) / len(scores)

# ❌ 可以不用:简单变量
x = 10  # 很明显是整数,不需要类型提示

重要提醒

类型提示是可选的!

python
# 这两种写法都是正确的
name: str = "张三"  # 有类型提示
name = "张三"        # 没有类型提示

Python 不会强制检查类型:

python
age: int = "十八"  # 这行代码可以运行,但不符合类型提示

类型提示的作用:

  • 帮助人类理解代码
  • 帮助编辑器提供提示
  • 不是强制性的检查

小结

类型提示是"注释",不是"规则"
├── 让代码更易读
├── 帮助编辑器更智能
├── 不影响程序运行
└── 是可选的,不是必需的

L2 实践层:用好

推荐做法

做法原因示例
isinstance() 而非 type() 做类型检查支持继承,更灵活isinstance(x, (int, str))
is None 而非 == None 检查空值None 是单例,is 检查身份,不会被子类重载if value is None:
is 判断身份,用 == 判断值语义清晰,各司其职a is b 检查同一对象 / a == b 检查值相等
类型提示用于函数签名提高可读性和 IDE 支持def greet(name: str) -> str:
多变量赋值使用元组解包一行完成,Python 风格a, b = b, a
变量命名使用 snake_casePython 社区约定user_name 而非 userName
变量值交换用 a, b = b, a无需临时变量,Python 特有a, b = b, a
检查空容器用 if not items:惯用法,比 if len(items) == 0: 简洁if not items:

反模式:不要这样做

python
# ❌ 错误:用 type() 做类型检查(不支持继承)
if type(obj) == Animal:
    process(obj)

# ✅ 正确:用 isinstance()(支持继承)
if isinstance(obj, Animal):
    process(obj)
python
# ❌ 错误:用 == 判断 None
if value == None:
    ...

# ✅ 正确:用 is 判断 None
if value is None:
    ...
python
# ❌ 错误:用 is 比较数值(小整数池导致不可靠行为)
if x is 100:
    ...

# ✅ 正确:用 == 比较数值
if x == 100:
    ...
python
# ❌ 错误:变量名含连字符或数字开头
user-name = "Bob"   # SyntaxError
2name = "Alice"     # SyntaxError

# ✅ 正确:使用蛇形命名法
user_name = "Bob"
name2 = "Alice"
python
# ❌ 错误:检查空列表用 len() 或 == []
if len(items) == 0:
    ...
if items == []:
    ...

# ✅ 正确:用真值判断惯用法
if not items:
    ...
python
# ❌ 错误:用 not 判断 None(无法区分 None 和空值)
def process(value: str | None) -> str:
    if not value:  # None 和 '' 都会进入这里!
        return "空值"
    return value

# ✅ 正确:明确区分 None 和其他假值
def process(value: str | None) -> str:
    if value is None:
        return "无数据"
    if not value:
        return "空字符串"
    return value

常见陷阱

陷阱说明解决方案
小整数池缓存-5~256 的小整数 is 可能返回 True,但不保证数值比较用 ==,不用 is
浮点精度误差0.1 + 0.2 != 0.3round()math.isclose()
可变默认参数函数默认参数 [] 在所有调用间共享None 替代默认值
浅拷贝引用list2 = list1 不创建新对象list(list1)copy.copy()
boolint 子类True == 1, False == 0,可能在类型检查中意外匹配isinstance(x, bool) 先判断 bool
字符串驻留短字符串可能被缓存,is 可能意外返回 True字符串比较一律用 ==
变量类型动态变化x = 10x = "str" 合法但易出错保持变量类型一致性

适用场景

场景是否推荐原因
函数参数和返回值✅ 推荐类型提示接口清晰,IDE 友好
简单临时变量❌ 可不加类型提示类型一目了然
判断 None✅ 推荐 is None最安全、最 Pythonic
判断数值相等✅ 推荐 ==比较值而非身份
需要多变量交换✅ 推荐 a, b = b, aPython 特有语法,一行完成
判断变量类型✅ 推荐 isinstance()支持继承,更灵活
需要知道具体类型对象✅ 用 type()返回类型对象,适合动态创建
检查空容器(列表/字典/字符串)✅ 推荐 if not x:Python 惯用法
处理可选值(可能为 None)✅ 推荐 `strNone` 类型提示
金融/科学计算❌ 不推荐直接 float 比较decimal.Decimalmath.isclose()

L3 专家层:深入

Python 如何实现变量

Python 变量不是"值容器",而是"名标签"——变量名通过引用指向内存中的对象。

Python 变量引用模型:
┌─────────────────────────────────────────────────────────────┐
│          变量不是盒子,是标签                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ❌ C/Java 模型(变量=盒子):                                 │
│  ┌───┐                                                      │
│  │ 10│  x = 10    → 变量 x 存储了值 10                       │
│  └───┘                                                      │
│                                                             │
│  ✅ Python 模型(变量=标签):                                 │
│  堆内存对象                栈帧名称空间                       │
│  ┌──────┐              ┌──────┐                              │
│  │  10  │  ←───────   │  x   │  x = 10  → 标签 x 贴在对象上  │
│  │ref=1 │              └──────┘                              │
│  └──────┘                                                    │
│                                                             │
│  x = 20  →  标签从旧对象撕下,贴到新对象上                     │
│              旧对象 refcount 减 1,若变为 0 则被回收          │
│                                                             │
│  引用计数(CPython):每个 PyObject 携带 ob_refcnt 字段          │
│  ob_refcnt = 0 → 对象被垃圾回收                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

验证代码:

python
import sys

# 引用计数
a = "hello"
print(sys.getrefcount(a))  # 引用计数(含 getrefcount 自身临时引用)

# 小整数缓存:-5 到 256
x = 256
y = 256
print(x is y)  # True(命中小整数池)

x = 257
y = 257
print(x is y)  # 在 CPython 中通常 True(脚本级编译优化),但不应依赖

# 字符串驻留(intern)
a = "hello"
b = "hello"
print(a is b)  # True(短字符串常量自动驻留)

a = "hello world!"
b = "hello world!"
print(a is b)  # True(标识符格式字符串自动驻留)

# 查看变量绑定的字节码
from dis import dis
dis("x = 10")
# LOAD_CONST    0 (10)
# STORE_NAME    0 (x)

# Float 的 IEEE 754 精度问题
f = 0.1 + 0.2
print(repr(f))           # 0.30000000000000004
print(f == 0.3)          # False
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))  # 0.3

类型系统内部实现

类型检查的内部机制:
┌─────────────────────────────────────────────────────────────┐
│          type() vs isinstance() 实现差异                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  type(obj) 直接返回 obj.__class__                             │
│  → type(obj) == SomeClass → 只比较类对象本身                  │
│                                                             │
│  isinstance(obj, SomeClass) 遍历 MRO:                        │
│  → SomeClass in type(obj).__mro__ ?                         │
│  → 支持鸭子类型(通过 __instancecheck__ 协议)                 │
│                                                             │
│  None 是单例:                                                │
│  → NoneType 只有一个实例,全局共享                            │
│  → None.__class__ 是 NoneType                                │
│  → 因此用 is 而非 ==(后者可被 __eq__ 重载)                   │
│                                                             │
│  bool 是 int 子类:                                           │
│  → issubclass(bool, int)  # True                             │
│  → True == 1, False == 0                                     │
│  → 但 type(True) is bool,type(1) is int                      │
│  → isinstance(True, int) 也是 True — 这是陷阱!               │
│  → 如需区分 bool 和 int,先用 type(x) is bool                 │
│                                                             │
│  可变对象 vs 不可变对象:                                      │
│  → 不可变:int, float, str, tuple, frozenset, bool, None     │
│  → 可变:list, dict, set, bytearray                          │
│  → 不可变对象修改 = 创建新对象;可变对象修改 = 原地修改        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

验证代码:

python
# 查看 MRO(方法解析顺序)
print(int.__mro__)
# (<class 'int'>, <class 'object'>)

print(bool.__mro__)
# (<class 'bool'>, <class 'int'>, <class 'object'>)

# bool 是 int 子类的验证
print(issubclass(bool, int))   # True
print(isinstance(True, int))   # True ← 注意!
print(isinstance(True, bool))  # True

# 可变 vs 不可变
x = 10
print(id(x))   # 例如 4304901232
x = x + 1      # 创建新对象
print(id(x))   # 不同的 id!

lst = [1, 2]
print(id(lst))  # 例如 4372290112
lst.append(3)   # 原地修改
print(id(lst))  # 相同 id!

性能考量

操作复杂度说明
变量赋值 x = valueO(1)引用绑定,无拷贝
isinstance(x, T)O(n)n = MRO 长度,通常 ≤ 3
type(x) == TO(1)直接属性访问
小整数 x is y (≤256)O(1)命中缓存,同一对象
大整数 x is y (>256)不保证取决于编译优化
浮点运算取决于精度受 IEEE 754 影响
字符串拼接(少量)O(n)CPython 有优化
字符串拼接(循环)O(n²)"".join() 优化到 O(n)
python
import timeit

# isinstance vs type 性能
print(timeit.timeit("isinstance(42, int)", number=10_000_000))
# → ~0.5 秒

print(timeit.timeit("type(42) is int", number=10_000_000))
# → ~0.4 秒(略快,但不考虑继承)

# 变量赋值的代价(极小)
print(timeit.timeit("x = 42", number=10_000_000))
# → ~0.2 秒

知识关联

变量与数据类型知识关联:

                    ┌─────────────┐
                    │  Python对象 │
                    │   模型      │
                    │ PyObject   │
                    └─────────────┘

          ┌───────────────┼───────────────┐
          ↓               ↓               ↓
    ┌───────────┐  ┌─────────────┐  ┌───────────┐
    │ 引用计数  │  │  垃圾回收   │  │ 小整数池  │
    │ refcount  │  │    GC       │  │ -5 ~ 256 │
    └───────────┘  └─────────────┘  └───────────┘


    ┌───────────┐     ┌─────────────┐     ┌───────────┐
    │ id()      │────→│  is / is not │←───│ NoneType  │
    │ 内存地址  │     │  身份判断    │     │ 单例模式  │
    └───────────┘     └─────────────┘     └───────────┘


    ┌───────────┐     ┌─────────────┐
    │  == / !=  │     │ isinstance  │
    │  值比较   │     │  MRO 遍历   │
    └───────────┘     └─────────────┘


    ┌───────────────┐
    │  不可变类型   │  int, float, str, tuple, frozenset, bool, None
    │  vs 可变类型  │  list, dict, set, bytearray
    └───────────────┘

本章小结

┌─────────────────────────────────────────────────────────────┐
│                  变量与数据类型 知识要点                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   变量命名:                                                 │
│   ✓ 字母/数字/下划线,不能以数字开头                        │
│   ✓ 使用 snake_case 命名风格                                │
│                                                             │
│   数据类型:                                                 │
│   ✓ int(整数)、float(浮点数)                            │
│   ✓ str(字符串)、bool(布尔值)                           │
│   ✓ NoneType(空值):None                                  │
│                                                             │
│   类型转换:                                                 │
│   ✓ int()、float()、str()、bool()                          │
│                                                             │
│   类型提示:                                                 │
│   ✓ 基本语法:变量名: 类型 = 值                             │
│   ✓ 函数注解:def func(x: int) -> str:                     │
│   ✓ 是可选的,让代码更易读                                  │
│                                                             │
│   类型检查:                                                 │
│   ✓ isinstance() 检查类型                                  │
│   ✓ type() 查看具体类型                                    │
│                                                             │
│   None 类型:                                                │
│   ✓ 表示"空"或"无"                                          │
│   ✓ 判断用 is None,不用 == None                           │
│   ✓ Optional[str] 等价于 str | None                        │
│                                                             │
│   对象标识:                                                 │
│   ✓ id() 获取对象唯一标识                                   │
│   ✓ is 判断是否同一对象                                     │
│   ✓ == 判断值是否相等                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘