四、函数
4.1 函数定义与调用
python
def greet(name, greeting="Hello"):
"""向指定的人打招呼(这是文档字符串 docstring)
Args:
name: 名字
greeting: 问候语,默认为 "Hello"
Returns:
完整的问候语字符串
"""
return f"{greeting}, {name}!"
# 调用
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hi")) # Hi, Bob!
# 查看文档
print(greet.__doc__)
help(greet) # 交互式查看
# Python 函数是一等公民:可赋值给变量、作为参数传递、作为返回值返回
def square(x):
return x * x
func = square # 赋值给变量
print(func(5)) # 25
def apply(func, value): # 函数作为参数
return func(value)
print(apply(square, 6)) # 36
4.2 参数类型(重要面试考点)
参数定义顺序规范:
python
位置参数 → 默认参数 → *args → 仅限关键字参数 → **kwargs
位置参数
python
def add(a, b): # a 和 b 都是位置参数(必选)
return a + b
add(3, 5) # 正常调用
# add(3) # ❌ TypeError: missing 1 required positional argument
默认参数(注意可变对象陷阱)
python
# ⚠️ 危险写法:默认参数使用可变对象
def bad_append(item, target=[]):
target.append(item)
return target
print(bad_append(1)) # [1]
print(bad_append(2)) # [1, 2] --- 不是期望的 [2]!
print(bad_append(3)) # [1, 2, 3] --- 默认列表在多次调用间共享!
# ✅ 正确写法
def good_append(item, target=None):
if target is None:
target = []
target.append(item)
return target
print(good_append(1)) # [1]
print(good_append(2)) # [2] --- 符合预期
*args --- 可变位置参数
python
def sum_all(*args):
"""接受任意数量的位置参数"""
print(f"参数个数: {len(args)}, 参数: {args}")
return sum(args)
print(sum_all(1, 2, 3, 4, 5)) # 参数个数: 5, 参数: (1, 2, 3, 4, 5)
# 调用时解包
nums = [1, 2, 3]
print(sum_all(*nums)) # 等价于 sum_all(1, 2, 3)
**kwargs --- 可变关键字参数
python
def print_info(**kwargs):
"""接受任意数量的关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="北京")
# 调用时解包
config = {"host": "localhost", "port": 8080}
print_info(**config) # 等价于 print_info(host="localhost", port=8080)
仅限关键字参数(keyword-only arguments)
python
# 单独的 * 之后的参数只能用关键字方式传入
def create_user(name, age, *, city, phone):
return {"name": name, "age": age, "city": city, "phone": phone}
# ✅ 正确
user = create_user("Alice", 25, city="北京", phone="123456")
# ❌ 错误
# user = create_user("Alice", 25, "北京", "123456")
# TypeError: create_user() takes 2 positional arguments but 4 were given
位置限定参数(Python 3.8+)
python
# / 之前的参数只能用位置方式传入,不能用关键字
def func(a, b, /, c, d):
pass
func(1, 2, 3, 4) # ✅ 正确
func(1, 2, c=3, d=4) # ✅ 正确
# func(a=1, b=2, c=3, d=4) # ❌ a 和 b 不能用关键字传入
4.3 变量作用域 --- LEGB 规则
变量查找顺序:Local → Enclosing → Global → Built-in
python
x = "global" # Global
def outer():
x = "enclosing" # Enclosing
def inner():
x = "local" # Local
print(f"inner: {x}")
inner()
print(f"outer: {x}")
outer()
print(f"module: {x}")
# 输出:
# inner: local
# outer: enclosing
# module: global
global 关键字:
python
count = 0
def increment():
global count # 声明使用全局变量,而非创建局部变量
count += 1
increment()
print(count) # 1
nonlocal 关键字:
python
def outer():
x = 0
def inner():
nonlocal x # 声明使用外层函数的变量
x += 1
return x
return inner
counter = outer()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
4.4 lambda 匿名函数
python
# 语法:lambda 参数: 表达式(自动返回表达式结果)
# 常见用法1:作为 sort 的 key
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78},
]
sorted_students = sorted(students, key=lambda s: s["score"])
print(sorted_students)
# 常见用法2:配合 map / filter
nums = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, nums)) # [2, 4, 6, 8, 10]
positive = list(filter(lambda x: x > 0, [-1, 0, 2, 3])) # [2, 3]
4.5 装饰器 Decorator(高频面试考点)
概念: 在不修改原函数代码的前提下,为函数添加额外功能(遵循开闭原则)。
基本形式
python
import time
from functools import wraps
def timer(func):
"""测量函数执行时间的装饰器"""
@wraps(func) # ⭐ 保留原函数的元信息
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 执行耗时: {elapsed:.4f} 秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
# 调用
result = slow_function() # slow_function 执行耗时: 1.000x 秒
print(result) # "完成"
# @timer 等价于:slow_function = timer(slow_function)
带参数的装饰器(三层嵌套)
python
def repeat(times):
"""让函数重复执行指定次数的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
# Hello!
# Hello!
# Hello!
装饰器叠加
python
@timer
@repeat(2)
def greet():
time.sleep(0.5)
print("Hi")
greet()
# 执行顺序:timer 包裹 repeat 包裹 greet
# 输出:Hi (两次),然后打印总耗时约1秒
4.6 常用高阶函数
python
from functools import reduce
# map --- 对每个元素应用函数
nums = [1, 2, 3, 4, 5]
print(list(map(str, nums))) # ['1', '2', '3', '4', '5']
print(list(map(lambda x: x**2, nums))) # [1, 4, 9, 16, 25]
# filter --- 筛选出条件为真的元素
print(list(filter(lambda x: x % 2 == 0, nums))) # [2, 4]
print(list(filter(None, [0, "hi", "", None, 42]))) # ['hi', 42](过滤假值)
# reduce --- 累积计算
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])) # 15 (1+2+3+4+5)
print(reduce(lambda x, y: x * y, [1, 2, 3, 4])) # 24 (1*2*3*4)
# sorted --- 排序(返回新列表)
words = ["apple", "Banana", "cherry"]
print(sorted(words)) # ['Banana', 'apple', 'cherry'](ASCII顺序)
print(sorted(words, key=str.lower)) # ['apple', 'Banana', 'cherry'](不区分大小写)
print(sorted(words, key=len)) # ['apple', 'Banana', 'cherry'](按长度)
五、模块与包
5.1 模块
-
一个
.py文件就是一个模块,模块名 = 文件名(去掉.py) -
__name__属性:直接运行时为"__main__",被导入时为模块名
import 方式
python
# 方式一:导入整个模块
import math
print(math.sqrt(16)) # 4.0
# 方式二:使用别名
import numpy as np
print(np.array([1, 2, 3]))
# 方式三:导入特定内容
from datetime import datetime, timedelta
print(datetime.now())
# 方式四:导入并起别名
from collections import defaultdict as dd
# ⚠️ 不推荐:from module import *(污染命名空间)
if __name__ == "__main__": 的作用
python
# utils.py
def add(a, b):
return a + b
if __name__ == "__main__":
# 只有直接运行此文件时才执行
print("测试:", add(1, 2))
python
# 另一个文件 main.py
from utils import add # 不会触发 utils.py 中的测试代码
print(add(10, 20))
5.2 包
-
包含
__init__.py文件的目录就是一个包(Python 3.3+ 可为空文件) -
导入方式:
import package.module或from package import module
python
# 包结构示例
# mypackage/
# ├── __init__.py
# ├── core.py
# └── utils/
# ├── __init__.py
# └── helper.py
# 导入方式
import mypackage.core
from mypackage import core
from mypackage.utils import helper
# 相对导入(在包内部模块中使用)
from . import core # 同目录兄弟模块
from .core import some_func # 导入具体函数
from .. import other_package # 上级目录
5.3 虚拟环境
bash
# 创建虚拟环境
python -m venv myenv # Windows
python3 -m venv myenv # Unix/Mac
# 激活
# Windows: myenv\Scripts\activate
# Unix/Mac: source myenv/bin/activate
# 退出
deactivate
# 依赖管理
pip freeze > requirements.txt # 导出依赖列表
pip install -r requirements.txt # 安装所有依赖
# 常用 pip 命令
pip install package_name # 安装包
pip install package_name==1.2.3 # 安装指定版本
pip uninstall package_name # 卸载包
pip list # 列出已安装包
pip show package_name # 查看包详情
pip install --upgrade package_name # 升级包
六、面向对象编程(OOP)
6.1 类与实例
python
class Person:
"""这是一个 Person 类"""
species = "人类" # 类属性(所有实例共享)
def __init__(self, name, age):
"""构造方法,初始化实例属性"""
self.name = name # 实例属性
self.age = age
def introduce(self):
"""实例方法"""
return f"我叫{self.name},{self.age}岁"
# 创建实例
p1 = Person("Alice", 25)
p2 = Person("Bob", 30)
print(p1.introduce()) # 我叫Alice,25岁
print(p1.species) # 人类(访问类属性)
print(Person.species) # 人类(推荐通过类名访问类属性)
命名规范:
-
类名:大驼峰
PascalCase -
方法/属性:蛇形
snake_case -
私有:
_protected(约定)、__private(名称改写)
6.2 类属性与实例属性
python
class Counter:
total = 0 # 类属性,所有实例共享
def __init__(self):
# self.total += 1 # ⚠️ 这会创建实例属性,遮蔽类属性!
Counter.total += 1 # ✅ 通过类名访问,正确修改类属性
c1 = Counter()
c2 = Counter()
print(Counter.total) # 2
6.3 面向对象三大特性
封装
python
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # 受保护属性(约定)
@property
def balance(self): # getter
return self._balance
@balance.setter
def balance(self, amount): # setter(带验证)
if amount < 0:
raise ValueError("余额不能为负")
self._balance = amount
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
# 使用
acc = BankAccount("Alice", 1000)
print(acc.balance) # 1000 --- 像属性一样访问
acc.balance = 2000 # 调用 setter
# acc.balance = -500 # ❌ ValueError: 余额不能为负
acc.deposit(500)
print(acc.balance) # 2500
继承
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self): # 重写父类方法
return "汪汪!"
def fetch(self):
return f"{self.name} 去接球了"
class Cat(Animal):
def speak(self):
return "喵喵~"
# 使用
dog = Dog("旺财")
cat = Cat("小花")
print(dog.speak()) # 汪汪!
print(cat.speak()) # 喵喵~
# super() 调用父类方法
class Puppy(Dog):
def __init__(self, name, toy):
super().__init__(name) # 调用父类的 __init__
self.toy = toy
# 多继承与 MRO
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__) # D → B → C → A → object
多态(鸭子类型)
python
# Python 天生支持多态,不检查类型,只关注行为
def make_sound(animal):
print(animal.speak())
class Duck:
def speak(self):
return "嘎嘎"
class Dog:
def speak(self):
return "汪汪"
# 任何有 speak() 方法的对象都可以传入
make_sound(Duck()) # 嘎嘎
make_sound(Dog()) # 汪汪
6.4 魔术方法
python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self): # 面向开发者,用于调试
return f"Vector({self.x}, {self.y})"
def __str__(self): # 面向用户,可读性强
return f"({self.x}, {self.y})"
def __add__(self, other): # + 运算符
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other): # == 运算符
return self.x == other.x and self.y == other.y
def __len__(self): # len() 函数
return 2 # 假装二维向量长度固定为2
def __call__(self, factor): # 让实例可调用
return Vector(self.x * factor, self.y * factor)
def __getitem__(self, index): # 索引访问
return (self.x, self.y)[index]
# 使用
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(repr(v1)) # Vector(1, 2)
print(str(v1)) # (1, 2)
print(v1 + v2) # (4, 6)
print(v1 == v2) # False
print(len(v1)) # 2
print(v1(3)) # (3, 6) --- 调用实例
print(v1[0], v1[1]) # 1 2 --- 索引访问
6.5 @classmethod 和 @staticmethod
python
class Date:
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, date_str):
"""工厂方法:从字符串创建 Date 实例"""
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day)
@staticmethod
def is_valid_date(year, month, day):
"""工具函数:判断日期是否合法(不依赖类或实例)"""
if month < 1 or month > 12:
return False
if day < 1 or day > 31:
return False
return True
# 使用
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-06-01") # 工厂方法
print(d2.year, d2.month, d2.day) # 2024 6 1
print(Date.is_valid_date(2024, 2, 30)) # False
6.6 __slots__(内存优化)
python
class Point:
__slots__ = ('x', 'y') # 限制属性名,节省内存
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 # ❌ AttributeError: 不能动态添加属性
仅在需要创建海量实例时使用,普通场景不需要。
6.7 数据类(Python 3.7+)
python
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float
label: str = "默认点" # 带默认值的属性
# 自动生成 __init__, __repr__, __eq__ 等方法
p1 = Point(1.0, 2.0)
p2 = Point(1.0, 2.0)
print(p1) # Point(x=1.0, y=2.0, label='默认点')
print(p1 == p2) # True
# 处理可变默认值
@dataclass
class Student:
name: str
scores: list = field(default_factory=list) # ✅ 正确:用 default_factory
# 不可变数据类
@dataclass(frozen=True)
class FrozenPoint:
x: float
y: float
# fp = FrozenPoint(1, 2)
# fp.x = 3 # ❌ FrozenInstanceError