mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
2331 字
6 分钟
Python3 核心概念精讲:从基础到面向对象

Python3 核心概念精讲:从基础到面向对象#

本文从 Python 的对象模型出发,系统梳理数据结构、函数、装饰器、面向对象、异常处理等核心概念。每个知识点都配有实际开发中的最佳实践,帮助你写出更 Pythonic 的代码。


一、Python 对象模型:一切皆对象#

Python 中最重要的认知转变是:变量不是盒子,而是标签。

类型(对象)是内存中真实存在的实体,变量只是贴在对象上的名字。这就是为什么 Python 的变量可以随时指向不同类型的对象——因为变量本身没有类型,对象才有类型。

a = 10 # a 是一个标签,贴在 int 对象 10 上
a = "hello" # 同一个标签 a,现在贴到了 str 对象上
a = [1, 2] # 又贴到了 list 对象上

可变与不可变的本质区别#

类型不可变 (Immutable)可变 (Mutable)
代表int, str, tuple, frozensetlist, dict, set
修改行为创建新对象,标签重新指向原地修改同一对象
id() 变化变化(新对象)不变(同一对象)
# 不可变:重新赋值 = 标签换对象
a = 10
print(id(a)) # 140234866423056
a = 20
print(id(a)) # 140234866423376 ← 不同的对象
# 可变:修改 = 原地改对象
b = [1, 2, 3]
print(id(b)) # 140234851234560
b.append(4)
print(id(b)) # 140234851234560 ← 同一个对象!

Python 对象模型

最佳实践: 函数参数传递可变对象时,函数内部的修改会影响外部。防御性编程应在函数内部使用 data.copy() 或切片 data[:] 创建副本。


二、数据结构速览#

列表 (List)#

Python 的列表是动态数组,支持任意类型混合存储。

fruits = ["apple", "banana", "cherry"]
# 常用操作
fruits.append("date") # 末尾添加
fruits.extend(["fig", "grape"]) # 批量追加(注意和 append 的区别)
fruits.insert(1, "avocado") # 指定位置插入
fruits.pop() # 弹出末尾元素
fruits.pop(0) # 弹出指定位置

最佳实践: append 添加单个元素,extend 合并列表。误用 append 传入列表会产生嵌套:[1, [2, 3]] 而非 [1, 2, 3]。

字典 (Dict)#

键值对映射,键必须是不可变类型(因为需要哈希)。

user = {"name": "Alice", "age": 25}
# 安全取值
user.get("email", "未设置") # 不存在时返回默认值,不会抛 KeyError
# Python 3.9+ 合并语法
defaults = {"theme": "dark", "lang": "zh"}
config = defaults | user # 合并字典,右侧优先

最佳实践: 用 dict.get(key, default) 替代 dict[key],避免 KeyError。需要带默认值的字典用 collections.defaultdict。

元组 (Tuple)#

不可变的序列,常用于函数多返回值和作为字典的键。

# 函数返回多个值本质是返回元组
def get_user():
return "Alice", 25, "alice@example.com"
name, age, email = get_user() # 解包

最佳实践: 当数据不应被修改时用元组替代列表,既表达意图又略微提升性能。命名元组 collections.namedtuple 或 typing.NamedTuple 让元组更具可读性。


三、函数进阶#

参数类型全解#

Python 函数参数有四种传递方式,理解它们的优先级和组合规则至关重要:

def example(
name, # 必备参数(位置参数)
age=25, # 默认参数
*args, # 可变位置参数 → 打包为元组
**kwargs # 可变关键字参数 → 打包为字典
):
print(f"{name}, {age}")
print(f"额外位置参数: {args}")
print(f"额外关键字参数: {kwargs}")
example("Alice", 30, "extra1", "extra2", city="Beijing")
# Alice, 30
# 额外位置参数: ('extra1', 'extra2')
# 额外关键字参数: {'city': 'Beijing'}

最佳实践: 默认参数不要用可变对象!def f(data=[]) 是经典陷阱——所有调用共享同一个列表。正确写法:def f(data=None): data = data or []。

作用域与 global / nonlocal#

count = 0
def increment():
global count # 声明使用全局变量
count += 1
def outer():
x = 10
def inner():
nonlocal x # 声明使用外层函数的变量
x += 1
inner()
print(x) # 11

最佳实践: 尽量避免 global,它会让代码难以追踪和测试。如果需要共享状态,用类封装或者依赖注入。


四、模块与包#

import 的正确姿势#

# ✗ 不推荐:命名空间污染,不知道函数来自哪里
from os import *
# ✓ 推荐:明确来源
import os
os.path.join("/home", "user")
# ✓ 推荐:只导入需要的
from os.path import join, exists

__name__ 的作用#

每个模块都有 __name__ 属性。直接运行时值为 "__main__",被导入时值为模块名。

utils.py
def helper():
return "I'm a helper"
if __name__ == "__main__":
# 只在直接运行 utils.py 时执行
# 被其他模块 import 时不会执行
print(helper())

最佳实践: 所有脚本都应该有 if __name__ == "__main__" 守卫。这样模块既可以被导入复用,也可以独立运行测试。


五、推导式:Pythonic 的数据变换#

推导式是 Python 最优雅的特性之一,用一行代码完成数据的过滤和变换:

# 列表推导式
squares = [x**2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
# 字典推导式
word_lengths = {word: len(word) for word in ["hello", "world", "python"]}
# {'hello': 5, 'world': 5, 'python': 6}
# 集合推导式(自动去重)
unique_lengths = {len(word) for word in ["hi", "hello", "hey"]}
# {2, 5, 3}
# 生成器表达式(惰性求值,不占内存)
total = sum(x**2 for x in range(1000000))

最佳实践: 推导式应保持简洁。如果逻辑超过一行或需要多层嵌套,改用普通 for 循环更易读。嵌套推导式超过两层就是代码异味。


六、迭代器与生成器#

迭代器协议#

任何实现了 __iter__() 和 __next__() 方法的对象都是迭代器。for 循环的本质就是不断调用 next() 直到 StopIteration。

nums = [1, 2, 3]
it = iter(nums) # 获取迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# next(it) # StopIteration!

生成器:优雅的迭代器#

生成器是创建迭代器的简洁方式。函数中包含 yield 关键字就自动变成生成器函数。

yield 的三个关键行为:

  1. 产出值并暂停 — 返回值给调用者,然后冻结在当前位置
  2. 保留运行状态 — 所有局部变量、执行位置都被保存
  3. 恢复执行 — 下次 next() 从暂停处继续
def countdown(n):
while n > 0:
yield n # 产出 n,暂停
n -= 1 # 下次 next() 从这里继续
gen = countdown(5)
print(next(gen)) # 5
print(next(gen)) # 4
for val in gen: # 继续迭代剩余的
print(val) # 3, 2, 1

迭代器 vs 生成器

最佳实践: 处理大数据集时,生成器是救星。读取 10GB 日志文件:用 for line in open("huge.log") 而非 open("huge.log").readlines(),前者逐行生成,后者一次性加载到内存。


七、with 语句与上下文管理器#

with 语句确保资源(文件、锁、连接)在使用后被正确释放,即使发生异常。

# 文件操作:with 自动关闭文件
with open("data.txt", "r") as f:
content = f.read()
# 离开 with 块后,f 自动关闭,即使中间抛了异常
# 同时管理多个资源
with open("input.txt") as infile, open("output.txt", "w") as outfile:
outfile.write(infile.read().upper())

自定义上下文管理器#

实现 __enter__ 和 __exit__ 方法,或使用 contextlib.contextmanager 装饰器:

from contextlib import contextmanager
import time
@contextmanager
def timer(label):
start = time.perf_counter()
yield # 这里是 with 块内的代码执行点
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}s")
with timer("数据处理"):
data = [x**2 for x in range(1000000)]
# 输出: 数据处理: 0.0823s

最佳实践: 任何需要”获取-使用-释放”模式的场景都应该用 with:文件、数据库连接、线程锁、临时目录。contextlib 模块提供了 suppress、redirect_stdout 等实用工具。


八、Lambda 表达式#

Lambda 是匿名的单行函数,适合作为高阶函数的参数:

# 排序:按字典的 age 字段
users = [{"name": "Bob", "age": 30}, {"name": "Alice", "age": 25}]
sorted_users = sorted(users, key=lambda u: u["age"])
# 配合 map/filter
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16, 25]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]

最佳实践: Lambda 只用于简单的单行逻辑。如果需要多行或复杂逻辑,定义具名函数更清晰。PEP 8 明确建议不要把 lambda 赋值给变量(f = lambda x: x+1),直接用 def 。


九、装饰器深入#

装饰器是 Python 最强大的元编程工具之一。它允许在不修改原函数代码的前提下,动态增强函数的功能。

函数装饰器的本质#

@decorator 是语法糖,等价于 func = decorator(func)。装饰器接收一个函数,返回一个新函数(通常是 wrapper)。

import functools
def log_calls(func):
@functools.wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
print(f"→ 调用 {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f"← {func.__name__} 返回 {result}")
return result
return wrapper
@log_calls
def add(a, b):
"""两数相加"""
return a + b
add(3, 5)
# → 调用 add((3, 5), {})
# ← add 返回 8

理解 return wrapper vs return wrapper()#

这是初学者最容易混淆的点:

  • return wrapper — 返回函数本体(遥控器),以后调用时才执行
  • return wrapper() — 立即执行函数,返回执行结果

装饰器必须返回函数本体,否则装饰后的函数就不是函数了。

带参数的装饰器(三层嵌套)#

当装饰器本身需要接收参数时,需要多包一层:

import functools
def retry(max_times=3, delay=1):
"""带参数的重试装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_times + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {attempt} 次失败: {e}")
if attempt == max_times:
raise
time.sleep(delay)
return wrapper
return decorator
@retry(max_times=3, delay=2)
def fetch_data(url):
# 网络请求逻辑...
pass

用类实现装饰器#

当装饰器需要管理复杂状态(如计数、缓存)时,用类实现更清晰:

class CallCounter:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 已被调用 {self.count} 次")
return self.func(*args, **kwargs)
@CallCounter
def process():
pass
process() # process 已被调用 1 次
process() # process 已被调用 2 次

__call__ 方法让类的实例可以像函数一样被调用,它就是类装饰器中的 wrapper。

装饰器执行机制

最佳实践:

  1. 始终使用 @functools.wraps(func) — 保留原函数的 __name__、__doc__ 等元信息
  2. wrapper 必须接受 *args, **kwargs — 确保兼容任意函数签名
  3. 常用内置装饰器: @property、@classmethod、@staticmethod、@functools.lru_cache

十、面向对象编程#

面向对象是 Python 的核心编程范式。Python 的 OOP 哲学可以用一句话概括:一切皆对象,鸭子类型优先。

类的基本结构#

class User:
"""用户类"""
# 类属性:所有实例共享
platform = "MyApp"
def __init__(self, name: str, email: str):
# 实例属性:每个实例独立
self.name = name
self.email = email
self._login_count = 0 # 约定私有(单下划线)
self.__password_hash = "" # 名称改写(双下划线)
def login(self):
"""普通方法:操作实例状态"""
self._login_count += 1
return f"{self.name} 登录成功(第 {self._login_count} 次)"
@property
def login_count(self):
"""属性装饰器:像属性一样访问,但有控制逻辑"""
return self._login_count
@classmethod
def from_dict(cls, data: dict):
"""类方法:备用构造函数"""
return cls(data["name"], data["email"])
@staticmethod
def validate_email(email: str) -> bool:
"""静态方法:纯工具函数"""
return "@" in email and "." in email

封装:保护数据完整性#

Python 没有真正的 private 关键字,而是通过命名约定实现封装:

  • _name — 约定私有,外部不应直接访问(但技术上可以)
  • __name — 名称改写(Name Mangling),变成 _ClassName__name,更强的保护
  • name_ — 避免与关键字冲突,如 class_、type_
class BankAccount:
def __init__(self, balance: float):
self.__balance = balance # 双下划线保护
@property
def balance(self) -> float:
"""只读属性"""
return self.__balance
def deposit(self, amount: float):
if amount <= 0:
raise ValueError("存款金额必须为正数")
self.__balance += amount
def withdraw(self, amount: float):
if amount > self.__balance:
raise ValueError("余额不足")
self.__balance -= amount

继承与方法重写#

class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
raise NotImplementedError("子类必须实现 speak 方法")
class Dog(Animal):
def speak(self) -> str:
return f"{self.name}: 汪汪!"
class Cat(Animal):
def speak(self) -> str:
return f"{self.name}: 喵~"
# 多态:同一接口,不同行为
animals = [Dog("旺财"), Cat("咪咪")]
for animal in animals:
print(animal.speak())

多继承与 MRO#

Python 支持多继承,方法解析顺序(MRO)遵循 C3 线性化算法:

class A:
def greet(self):
return "A"
class B(A):
def greet(self):
return "B"
class C(A):
def greet(self):
return "C"
class D(B, C):
pass
d = D()
print(d.greet()) # "B" — 按 MRO 顺序
print(D.__mro__) # (D, B, C, A, object)

super() 的正确用法#

class Student(User):
def __init__(self, name: str, email: str, grade: int):
super().__init__(name, email) # 调用父类构造
self.grade = grade
def login(self):
result = super().login() # 调用父类方法
return f"{result}(学生用户)"

鸭子类型:Python 的多态哲学#

Python 不需要显式的接口声明。只要对象有正确的方法,就可以使用:

class FileLogger:
def write(self, msg):
with open("app.log", "a") as f:
f.write(msg + "\n")
class ConsoleLogger:
def write(self, msg):
print(f"[LOG] {msg}")
def log_message(logger, msg):
"""不关心 logger 的类型,只要有 write 方法就行"""
logger.write(msg)
# 两种 logger 都能用,这就是鸭子类型
log_message(FileLogger(), "保存到文件")
log_message(ConsoleLogger(), "打印到控制台")

面向对象三大支柱

类方法装饰器对比

最佳实践:

  1. 优先组合而非继承 — 继承层级不超过 3 层,复杂关系用 “has-a” 替代 “is-a”
  2. 使用 ABC 定义接口 — from abc import ABC, abstractmethod 强制子类实现特定方法
  3. 单一职责 — 每个类只做一件事,如果类名里有 “And”,说明该拆分了
  4. 用 dataclass 简化数据类 — Python 3.7+ 的 @dataclass 自动生成 __init__、__repr__ 等

十一、异常处理#

在 AI 辅助编程时代,Debug 能力是重中之重。异常处理不仅是捕获错误,更是一种防御性编程策略。

完整的异常处理结构#

try:
result = 10 / int(input("输入除数: "))
except ValueError:
print("请输入有效的数字")
except ZeroDivisionError:
print("除数不能为零")
except Exception as e:
print(f"未预期的错误: {e}")
else:
# 只在 try 块没有异常时执行
print(f"结果: {result}")
finally:
# 无论如何都会执行(资源清理)
print("计算结束")

异常的传播规则#

如果异常在 try(或 except、else)中被抛出,且没有被任何 except 捕获,它会在 finally 执行完毕后继续向上传播。

自定义异常#

class AppError(Exception):
"""应用基础异常"""
pass
class ValidationError(AppError):
"""数据验证异常"""
def __init__(self, field: str, message: str):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
class NotFoundError(AppError):
"""资源未找到"""
pass
# 使用
def create_user(name: str, age: int):
if not name:
raise ValidationError("name", "用户名不能为空")
if age < 0 or age > 150:
raise ValidationError("age", "年龄必须在 0-150 之间")

raise 的三种用法#

# 1. 抛出新异常
raise ValueError("无效的输入")
# 2. 重新抛出当前异常(保留原始堆栈)
try:
risky_operation()
except Exception:
logging.error("操作失败")
raise # 不带参数,原样抛出
# 3. 异常链(Python 3)
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
raise ValidationError("data", "JSON 格式错误") from e

异常处理流程

最佳实践:

  1. 永远不要裸 except: — 至少用 except Exception,否则会吞掉 KeyboardInterrupt 和 SystemExit
  2. 精确捕获 — except ValueError 优于 except Exception,只处理你知道如何处理的异常
  3. 自定义异常继承 Exception — 不要继承 BaseException
  4. 用 raise ... from e 保留异常链 — 方便调试时追溯根因
  5. 日志记录用 logging.exception() — 自动包含堆栈信息

十二、类型注解#

Python 3.5+ 引入的类型注解不影响运行时行为,但能极大提升代码可读性和 IDE 支持:

from typing import Optional, Union
def find_user(user_id: int) -> Optional[dict]:
"""查找用户,不存在返回 None"""
...
def process(data: list[dict[str, Union[str, int]]]) -> float:
"""处理数据列表"""
...
# Python 3.10+ 更简洁的语法
def greet(name: str | None = None) -> str:
return f"Hello, {name or 'World'}"

最佳实践:

  1. 渐进式采用 — 从公共接口和复杂函数开始,不需要一次性全部注解
  2. 配合 mypy 使用 — pip install mypy && mypy your_project/ 静态类型检查
  3. 避免过度注解 — x: int = 5 是多余的,类型显而易见时可以省略

总结#

Python 的核心哲学是 “简洁优于复杂,可读性至上”。掌握这些核心概念后,关键是在实际项目中不断实践:

  • 对象模型 让你理解 Python 的运行机制
  • 装饰器 让你写出优雅的横切关注点代码
  • 面向对象 让你构建可维护的大型系统
  • 异常处理 让你的代码在生产环境中稳健运行

记住:Pythonic 的代码不是炫技,而是让下一个读代码的人(包括未来的你)能快速理解意图。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Python3 核心概念精讲:从基础到面向对象
https://shaoyou01.github.io/blogs/python3-core-guide/
作者
shaoyou
发布于
2026-02-25
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时