人工智能的定义是:让计算机去做人类需要智慧才能做的事。
📝 变量与数据类型
变量是程序中存储数据的容器,类似于贴在数据上的"标签"。Python 的变量机制相当灵活:同一个变量可以在不同时刻引用不同类型的对象。本节涵盖变量命名规则、赋值方式、内置基本数据类型,以及类型注解的基础用法。
Python 的变量为什么能这么灵活?答案在于它的动态类型机制——变量本身不携带类型信息,类型属于对象。接下来从命名规则开始。
📌 本节要点
- 变量命名规则与 PEP 8 推荐风格
- 动态类型:变量无类型,对象才有类型
- 用
type()和isinstance()查看与判断类型 - 6 种基本数据类型:int、float、complex、str、bool、NoneType
- 多重赋值与解包赋值(含星号
*收集) - Python 3.10+ 类型注解基础:
int | str、list[str]
变量命名规则
Python 变量命名有以下约束:
- 只能由字母、数字、下划线组成
- 不能以数字开头
- 区分大小写(
age与Age是不同的变量) - 不能使用 Python 关键字(如
if、for、class等)
# 合法命名
name = "Alice"
user_age = 18
_private = "秘密"
student2 = "Bob"
userName = "Charlie" # 驼峰命名(Python 中更推荐下划线命名 snake_case)
# 非法命名(取消注释会报错)
# 2user = "Tom" # 不能以数字开头
# user-name = "Tom" # 不能包含连字符
# for = 10 # 不能使用关键字
Python 官方风格指南 PEP 8 推荐使用 snake_case(下划线小写)命名变量与函数,如 user_age、max_count;类名使用 CamelCase(如 MyClass);常量使用全大写(如 MAX_VALUE)。
查看所有关键字
keyword 模块可以列出 Python 的全部保留关键字:
import keyword
print(keyword.kwlist)
# ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', ...]
print(len(keyword.kwlist)) # 当前关键字数量
动态类型
Python 是动态类型语言,意味着变量可以随时改变"身份"。
x = 10 # x 现在是一个整数
print(type(x)) # <class 'int'>
x = "hello" # 现在 x 变成了字符串
print(type(x)) # <class 'str'>
x = [1, 2, 3] # 现在 x 变成了列表
print(type(x)) # <class 'list'>
Python 中,变量没有类型,对象才有类型。变量只是一个标签,可以随时绑定到不同类型的对象上。
Python 的类型系统
Python 的类型系统可以从两个维度来理解:类型检查时机(静态 vs 动态)和类型转换规则(强 vs 弱)。为什么需要理解这两个维度?因为它们决定了代码的行为方式和错误暴露时机。
静态类型 vs 动态类型
这个维度回答:类型信息在什么时候确定?
静态类型(Java、C、Rust):变量声明时必须指定类型,编译器在编译阶段就检查类型是否匹配,运行时不能改变。
# Java 等静态类型语言(伪代码对比)
# int x = 10; // 声明 x 为 int
# x = "hello"; // ❌ 编译错误:不能把 str 赋给 int 变量
动态类型(Python、Ruby、JavaScript):变量不需要声明类型,类型在运行时根据赋值自动确定,同一变量可以随时引用不同类型的对象。
x = 10 # x 引用 int
x = "hello" # ✅ 合法,x 现在引用 str
x = [1, 2, 3] # ✅ 合法,x 现在引用 list
动态类型让代码更简洁——不需要写类型声明,快速迭代时非常方便。代价是类型错误要到运行时才会暴露。Python 3.5+ 引入的类型注解(Type Hints)让我们可以选择性地添加类型信息,兼顾灵活性和安全性。
强类型 vs 弱类型
这个维度回答另一个问题:不同类型混用时会发生什么?
强类型(Python、Java、Rust):不同类型之间不会进行隐式转换,混用直接报错。
"1" + 1 # TypeError: can only concatenate str to str (not "int")
"1" * 3 # "111" — 合法,因为 str * int 是 Python 明确定义的操作
弱类型(JavaScript、Perl、PHP):引擎会自动进行隐式类型转换,结果往往出人意料。
// JavaScript(弱类型)
"1" + 1 // "11" — 数字被隐式转为字符串
"1" - 1 // 0 — 字符串被隐式转为数字
true + 1 // 2 — 布尔值被隐式转为数字
弱类型的隐式转换是大量 bug 的根源。"1" + 1 在 JavaScript 中得到 "11" 而不是 2,这种行为很容易造成困惑。Python 的强类型策略让这类错误在运行时立即暴露,而不是悄悄产生错误结果。
四象限总览
把两个维度组合起来,编程语言可以分成四类:
| 强类型 | 弱类型 | |
|---|---|---|
| 静态类型 | Java、Rust、Go、C# | C(隐式指针转换)、Go(部分隐式转换) |
| 动态类型 | Python、Ruby、Haskell | JavaScript、Perl、PHP |
Python 的定位:动态 + 强类型。 这意味着:
- 变量不需要声明类型(动态)
- 但不同类型不能隐式混用(强)
Python 的有限例外
Python 虽然是强类型,但有一些基于继承关系的隐式行为,这不是"弱类型转换",而是面向对象的设计:
# bool 是 int 的子类
True + 1 # 2(布尔值参与整数运算)
isinstance(True, int) # True
0 == False # True
1 == True # True
"" == False # True(空字符串等于 False)
# 这些是继承关系带来的行为,不是隐式类型转换
# bool 本质上就是 int 的特殊子类,True 就是 1,False 就是 0
渐进式类型注解
Python 独特的组合是:动态 + 强类型 + 可选类型注解。这三个特性各解决不同的问题:
# 动态:变量可以换类型
x = 10
x = "hello" # 合法
# 强类型:不同类型不能混用
# "1" + 1 # TypeError
# 可选注解:添加类型提示(不影响运行)
def greet(name: str) -> str:
return f"Hello, {name}"
# 类型注解不改变运行行为,只是给 IDE 和 mypy 看的
greet(123) # 运行时不报错,但 mypy 会警告
对比其他语言的组合:
- Java:静态 + 强类型 — 必须声明类型,不能混用,灵活度低但安全性高
- JavaScript:动态 + 弱类型 — 不声明类型,还能隐式转换,灵活度最高但 bug 最多
- Python:动态 + 强类型 + 可选注解 — 兼顾灵活性和安全性,是独特的"中间路线"
使用 type() 查看类型
查看对象类型最直接的方式是 type():
print(type(42)) # <class 'int'>
print(type(3.14)) # <class 'float'>
print(type("Python")) # <class 'str'>
print(type(True)) # <class 'bool'>
print(type(None)) # <class 'NoneType'>
如果要做"类型判断",推荐使用 isinstance() 而不是直接比较 type():
x = 5
# ❌ 不推荐:不支持子类判断
print(type(x) == int) # 输出: True
# ✅ 推荐:支持继承关系判断
print(isinstance(x, int)) # 输出: True
# isinstance 支持传入元组判断多种类型
print(isinstance(x, (int, float, str))) # 输出: True
| 场景 | 推荐 ✅ | 不推荐 ❌ |
|---|---|---|
| 类型判断 | isinstance(x, int) | type(x) == int |
| 多类型判断 | isinstance(x, (int, str)) | type(x) in (int, str) |
| 空值检查 | x is None | x == None |
isinstance() 会考虑继承关系,例如 True 既是 bool 也是 int 的实例(bool 是 int 的子类);而 type(True) == int 返回 False。一般做类型判断时优先用 isinstance()。
基本数据类型总览
Python 内置了6种基本数据类型,各司其职:
| 类型名 | 类型 | 示例 |
|---|---|---|
int | 整数 | 42, 0, -7, 1_000_000 |
float | 浮点数 | 3.14, -0.5, 2.0 |
complex | 复数 | 1+2j, 3j |
str | 字符串 | "hello", 'a' |
bool | 布尔值 | True, False |
NoneType | 空值 | None |
age = 25 # int
height = 1.75 # float
name = "Alice" # str
is_student = True # bool
result = None # NoneType
print(age, height, name, is_student, result)
# 输出: 25 1.75 Alice True None
print(type(age), type(height), type(name), type(is_student), type(result))
# 输出: <class 'int'> <class 'float'> <class 'str'> <class 'bool'> <class 'NoneType'>
Python 3.6+ 支持在数字字面量中使用下划线作为分隔符,提高大数字的可读性:1_000_000 等价于 1000000,0xFF_FF 等价于 0xFFFF。
多重赋值
Python 支持在一行内为多个变量同时赋值,这是非常实用的特性,尤其在交换变量值时格外方便:
# 同时给多个变量赋值
a, b, c = 1, 2, 3
print(a, b, c) # 输出: 1 2 3
# 交换两个变量的值(无需中间变量)
a, b = b, a
print(a, b) # 输出: 2 1
# 给多个变量赋同一个值
x = y = z = 0
print(x, y, z) # 输出: 0 0 0
多重赋值要求左右两侧的元素数量一致,否则会抛出 ValueError:
a, b = 1, 2, 3 # ValueError: too many values to unpack
解包赋值
解包(unpacking)是 Python 中非常优雅的特性,可以从任何可迭代对象中提取元素赋给多个变量。这种语法在处理函数返回值和数据结构时特别常用。
列表/元组解包
point = (3, 4)
x, y = point
print(x, y) # 3 4
colors = ["red", "green", "blue"]
r, g, b = colors
print(r, g, b) # red green blue
星号解包(*)
使用 * 可以收集剩余的元素为一个列表:
first, *rest = [1, 2, 3, 4, 5]
print(first) # 1
print(rest) # [2, 3, 4, 5]
*head, last = [1, 2, 3, 4, 5]
print(head) # [1, 2, 3, 4]
print(last) # 5
first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last) # 1 [2, 3, 4] 5
函数返回值解包
def get_user_info():
return "Alice", 25, "Beijing"
name, age, city = get_user_info()
print(name, age, city) # Alice 25 Beijing
类型注解基础
Python 依然是动态类型语言,但支持类型注解(Type Hints)——给变量贴上"我是数字"、"我是字符串"之类的标签。这些标签不会影响程序运行,但能让 IDE 和 mypy 等工具更好地理解代码意图。
变量类型注解
# 基本写法:变量名: 类型 = 值
name: str = "Alice"
age: int = 25
height: float = 1.75
is_active: bool = True
# 类型注解只是"提示",运行时仍可赋其他类型(但会被检查工具警告)
name = 123 # 运行不会报错,但 mypy 会警告
print(name)
类型注解不会在运行时强制校验类型,它只是给开发者和工具看的"说明"。要让类型真正被校验,需要使用 mypy 等静态检查工具。
Python 3.10+ 的联合类型语法
# Python 3.10+:使用 | 表示"或"类型
def process(value: int | str) -> str:
return f"处理: {value}"
print(process(42)) # 处理: 42
print(process("hello")) # 处理: hello
# 列表类型注解
from typing import Optional
def find_user(user_id: int) -> str | None:
if user_id == 1:
return "Alice"
return None
result: str | None = find_user(2)
print(result) # None
容器类型注解
from typing import Sequence
# 使用内置 list/dict/tuple/set 类型注解(Python 3.9+)
names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 90, "Bob": 85}
point: tuple[int, int] = (3, 4)
unique_ids: set[int] = {1, 2, 3}
print(names, scores, point, unique_ids)
实战:个人信息卡片
综合示例,将本节涉及的变量、多类型、解包与类型注解融合在一起:
def build_profile(name: str, age: int, height: float, tags: list[str]) -> dict:
"""构建用户信息字典"""
return {
"name": name,
"age": age,
"height": height,
"tags": tags,
"adult": age >= 18,
}
# 解包函数返回值
profile: dict = build_profile(
name="Alice",
age=25,
height=1.68,
tags=["python", "music"],
)
# 多重赋值 + 解包
name, age = profile["name"], profile["age"]
print(f"姓名: {name}, 年龄: {age}")
# 星号解包
*_, last_tag = profile["tags"]
print(f"最后一个标签: {last_tag}")
# 类型检查
print(f"profile 类型: {type(profile).__name__}")
print(f"age 是 int 吗: {isinstance(age, int)}")
运行结果:
姓名: Alice, 年龄: 25
最后一个标签: music
profile 类型: dict
age 是 int 吗: True
🎯 动手练习
以下练习有助于巩固本节内容:
- 命名规则验证:尝试使用非法命名(如
2user、user-name、for),观察 Python 的错误提示 - 类型探索:使用
type()查看以下值的类型:0、0.0、""、[]、{}、set() - 解包练习:创建一个包含 5 个元素的列表,使用星号解包分别获取第一个、中间三个、最后一个元素
- 类型注解实践:编写一个函数
calculate(a: int | float, b: int | float) -> int | float,实现加减乘除运算
📚 延伸阅读
- PEP 8 - Python 风格指南 - 官方推荐的代码风格规范
- PEP 484 - 类型提示 - Python 类型注解的正式规范
- PEP 604 - 联合类型语法 - Python 3.10+ 的
X | Y语法 - Python 关键字列表 - 官方保留字完整列表
name = value变量赋值age = 25a, b = b, a交换变量x, y = y, xfirst, *rest = lst星号解包1, [2,3,4,5]x: int = 10类型注解name: str = "Alice"`int \str`联合类型list[str]泛型容器names: list[str] = []isinstance(x, T)类型判断isinstance(x, int)type(x)查看类型type(42) # <class 'int'>✅ 本节总结
- Python 变量命名只能用字母、数字、下划线,且不能以数字开头,不能与关键字冲突
- Python 是动态类型语言:变量没有类型,对象才有类型;可用
type()查看,用isinstance()判断 - 基本数据类型包括
int、float、complex、str、bool、NoneType - 支持多重赋值和解包赋值(含星号
*收集),让代码更简洁 - Python 3.10+ 的类型注解(如
int | str、list[str])能显著提升代码可读性,配合mypy等工具可实现静态类型检查