Python 学习(第二部分:函数、模块与面向对象编程)

四、函数

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.modulefrom 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
相关推荐
gCode Teacher 格码致知1 小时前
Javascrip提高:CSS backdrop-filter的使用方法-由Deepseek产生
前端·css
铁皮哥1 小时前
【力扣题解】LeetCode 25. K 个一组翻转链表
java·数据结构·windows·python·算法·leetcode·链表
无盐海1 小时前
Foundatio,内存,Redis 缓存
数据库·redis·缓存
lbb 小魔仙1 小时前
告别腾讯会议40分钟限制:用ToDesk协作版开在线会议,免费不限时远程会议新方案
python·langchain·jenkins
六月雨滴1 小时前
Oracle 数据库内存管理
数据库·oracle
凯瑟琳.奥古斯特1 小时前
PyTorch动态计算图详解
人工智能·pytorch·python·深度学习
清灵xmf1 小时前
JS 原生深拷贝的终极方案——structuredClone
前端·javascript·vue.js·json.stringify·structuredclone
一个数据大开发2 小时前
企业知识工程的三条路线:Neo4j 知识中台、Agent + Action 与本体原生 Runtime
大数据·python·neo4j
甲方大人请饶命2 小时前
SSM-基础
java·数据库·spring