📌 目录

    第2课-Python基础回顾

    第2课:Python基础回顾

    课程目标

    通过本课程学习,你将能够:

    • 掌握Python面向对象编程的核心概念
    • 理解模块和包的导入机制
    • 掌握装饰器的原理和使用
    • 理解生成器和迭代器的工作原理
    • 掌握异常处理的最佳实践
    • 理解上下文管理器的应用场景
    • 为后续学习OneForAll项目打下坚实的Python基础

    2.1 面向对象编程基础

    2.1.1 类与对象

    什么是类?

    类(Class)是创建对象的模板或蓝图,它定义了一类对象的属性和方法。

    什么是对象?

    对象(Object)是类的实例,是根据类创建的具体实体。

    基本语法:

    # 定义一个类
    class Dog:
        # 类属性(所有实例共享)
        species = "Canis familiaris"
        
        # 初始化方法(构造函数)
        def __init__(self, name, age):
            # 实例属性(每个实例独有)
            self.name = name
            self.age = age
        
        # 实例方法
        def bark(self):
            return f"{self.name} says Woof!"
        
        def introduce(self):
            return f"My name is {self.name} and I'm {self.age} years old."
        
        # 类方法
        @classmethod
        def get_species(cls):
            return cls.species
        
        # 静态方法
        @staticmethod
        def is_dog(animal):
            return animal.lower() == "dog"
    
    
    # 创建对象(实例化)
    dog1 = Dog("Buddy", 3)
    dog2 = Dog("Max", 5)
    
    # 访问实例属性
    print(dog1.name)  # 输出: Buddy
    print(dog2.age)   # 输出: 5
    
    # 调用实例方法
    print(dog1.bark())        # 输出: Buddy says Woof!
    print(dog2.introduce())   # 输出: My name is Max and I'm 5 years old.
    
    # 访问类属性
    print(dog1.species)       # 输出: Canis familiaris
    
    # 调用类方法
    print(Dog.get_species())  # 输出: Canis familiaris
    
    # 调用静态方法
    print(Dog.is_dog("dog"))  # 输出: True
    

    2.1.2 继承

    什么是继承?

    继承允许一个类(子类)获取另一个类(父类)的属性和方法,实现代码复用和扩展。

    基本语法:

    # 父类(基类)
    class Animal:
        def __init__(self, name):
            self.name = name
        
        def speak(self):
            raise NotImplementedError("Subclass must implement abstract method")
        
        def eat(self):
            return f"{self.name} is eating."
    
    
    # 子类继承父类
    class Dog(Animal):
        def __init__(self, name, breed):
            # 调用父类的初始化方法
            super().__init__(name)
            self.breed = breed
        
        # 重写父类方法
        def speak(self):
            return f"{self.name} says Woof!"
        
        # 新增方法
        def fetch(self):
            return f"{self.name} is fetching the ball!"
    
    
    class Cat(Animal):
        def __init__(self, name, color):
            super().__init__(name)
            self.color = color
        
        def speak(self):
            return f"{self.name} says Meow!"
        
        def scratch(self):
            return f"{self.name} is scratching!"
    
    
    # 使用继承
    dog = Dog("Buddy", "Golden Retriever")
    cat = Cat("Whiskers", "Orange")
    
    print(dog.speak())   # 输出: Buddy says Woof!
    print(dog.eat())     # 输出: Buddy is eating.
    print(dog.fetch())   # 输出: Buddy is fetching the ball!
    
    print(cat.speak())   # 输出: Whiskers says Meow!
    print(cat.eat())     # 输出: Whiskers is eating.
    print(cat.scratch()) # 输出: Whiskers is scratching!
    

    2.1.3 多态

    什么是多态?

    多态允许不同类的对象对相同的方法调用做出不同的响应。

    class Animal:
        def speak(self):
            pass
    
    
    class Dog(Animal):
        def speak(self):
            return "Woof!"
    
    
    class Cat(Animal):
        def speak(self):
            return "Meow!"
    
    
    class Cow(Animal):
        def speak(self):
            return "Moo!"
    
    
    # 多态演示
    def animal_sound(animal):
        return animal.speak()
    
    
    # 创建不同的动物对象
    animals = [Dog(), Cat(), Cow()]
    
    # 相同的方法调用,不同的响应
    for animal in animals:
        print(animal_sound(animal))
    # 输出:
    # Woof!
    # Meow!
    # Moo!
    

    2.1.4 封装

    什么是封装?

    封装是隐藏对象的内部实现细节,只暴露必要的接口。

    class BankAccount:
        def __init__(self, owner, balance=0):
            self.owner = owner
            # 私有属性(以双下划线开头)
            self.__balance = balance
        
        # 公有方法
        def deposit(self, amount):
            if amount > 0:
                self.__balance += amount
                return f"Deposited ${amount}. New balance: ${self.__balance}"
            return "Invalid deposit amount."
        
        def withdraw(self, amount):
            if 0 < amount <= self.__balance:
                self.__balance -= amount
                return f"Withdrew ${amount}. New balance: ${self.__balance}"
            return "Invalid withdrawal amount or insufficient funds."
        
        # 只读属性访问器
        def get_balance(self):
            return self.__balance
        
        # 受保护属性(以单下划线开头)
        def _internal_method(self):
            return "This is an internal method"
    
    
    # 使用封装
    account = BankAccount("John", 1000)
    
    print(account.deposit(500))   # 输出: Deposited $500. New balance: $1500
    print(account.withdraw(200))  # 输出: Withdrew $200. New balance: $1300
    print(account.get_balance())  # 输出: 1300
    
    # 不能直接访问私有属性
    # print(account.__balance)  # 会报错: AttributeError
    
    # 但Python的私有只是名称改写
    print(account._BankAccount__balance)  # 可以访问(不推荐)
    

    2.1.5 特殊方法(魔法方法)

    常用的特殊方法:

    class Vector:
        def __init__(self, x, y):
            self.x = x
            self.y = y
        
        # 字符串表示
        def __str__(self):
            return f"Vector({self.x}, {self.y})"
        
        # 官方字符串表示
        def __repr__(self):
            return f"Vector({self.x}, {self.y})"
        
        # 相等运算
        def __eq__(self, other):
            if isinstance(other, Vector):
                return self.x == other.x and self.y == other.y
            return False
        
        # 加法运算
        def __add__(self, other):
            if isinstance(other, Vector):
                return Vector(self.x + other.x, self.y + other.y)
            raise TypeError("Operands must be of type Vector")
        
        # 长度(len函数)
        def __len__(self):
            return int((self.x ** 2 + self.y ** 2) ** 0.5)
        
        # 索引访问
        def __getitem__(self, index):
            if index == 0:
                return self.x
            elif index == 1:
                return self.y
            else:
                raise IndexError("Index out of range")
    
    
    # 使用特殊方法
    v1 = Vector(3, 4)
    v2 = Vector(1, 2)
    
    print(v1)              # 输出: Vector(3, 4)
    print(str(v1))         # 输出: Vector(3, 4)
    print(repr(v1))        # 输出: Vector(3, 4)
    print(v1 == v2)        # 输出: False
    print(v1 + v2)         # 输出: Vector(4, 6)
    print(len(v1))         # 输出: 5
    print(v1[0], v1[1])    # 输出: 3 4
    

    2.2 模块和包

    2.2.1 模块(Module)

    什么是模块?

    模块是一个包含Python定义和语句的文件,文件名就是模块名加上.py后缀。

    创建模块:

    # mymodule.py
    
    def greet(name):
        return f"Hello, {name}!"
    
    
    class Calculator:
        def add(self, a, b):
            return a + b
        
        def subtract(self, a, b):
            return a - b
    
    
    PI = 3.14159
    

    导入模块:

    # 方法1:导入整个模块
    import mymodule
    
    print(mymodule.greet("Alice"))
    calc = mymodule.Calculator()
    print(calc.add(5, 3))
    
    
    # 方法2:导入特定函数或类
    from mymodule import greet, Calculator
    
    print(greet("Bob"))
    calc = Calculator()
    print(calc.subtract(10, 4))
    
    
    # 方法3:导入所有内容(不推荐)
    from mymodule import *
    
    print(greet("Charlie"))
    print(PI)
    
    
    # 方法4:给模块起别名
    import mymodule as mm
    
    print(mm.greet("David"))
    
    
    # 方法5:给导入的内容起别名
    from mymodule import greet as say_hello
    
    print(say_hello("Eve"))
    

    2.2.2 包(Package)

    什么是包?

    包是一个包含多个模块的目录,目录中必须有一个__init__.py文件(Python 3.3+可以是空文件)。

    包的结构:

    mypackage/
    ├── __init__.py
    ├── module1.py
    ├── module2.py
    └── subpackage/
        ├── __init__.py
        └── module3.py
    

    创建包:

    # mypackage/__init__.py
    """
    This is the mypackage package.
    """
    
    from . import module1
    from . import module2
    
    __version__ = "1.0.0"
    
    
    # mypackage/module1.py
    def func1():
        return "Function 1 from module1"
    
    
    class Class1:
        def method(self):
            return "Method from Class1"
    
    
    # mypackage/module2.py
    def func2():
        return "Function 2 from module2"
    
    
    # mypackage/subpackage/__init__.py
    from . import module3
    
    
    # mypackage/subpackage/module3.py
    def func3():
        return "Function 3 from subpackage.module3"
    

    导入包:

    # 导入包
    import mypackage
    
    print(mypackage.__version__)
    
    
    # 导入包中的模块
    from mypackage import module1, module2
    
    print(module1.func1())
    print(module2.func2())
    
    
    # 导入包中的特定内容
    from mypackage.module1 import func1, Class1
    
    print(func1())
    obj = Class1()
    print(obj.method())
    
    
    # 导入子包
    from mypackage.subpackage import module3
    
    print(module3.func3())
    
    
    # 使用点号导入
    from mypackage.module1 import func1 as f1
    from mypackage.subpackage.module3 import func3 as f3
    
    print(f1())
    print(f3())
    

    2.2.3 __init__.py的作用

    __init__.py文件有以下作用:

    1. 标识目录为Python包
    2. 控制包的导入行为
    3. 提供包级别的初始化代码
    4. 定义__all__变量,控制from package import *的行为
    # mypackage/__init__.py
    
    # 定义包的公共接口
    from .module1 import func1, Class1
    from .module2 import func2
    
    # 定义__all__,控制import *的行为
    __all__ = ['func1', 'Class1', 'func2']
    
    # 包级别的变量
    __version__ = "1.0.0"
    __author__ = "Your Name"
    
    # 包初始化时执行的代码
    print(f"Initializing mypackage v{__version__}")
    
    
    # 现在可以这样导入
    from mypackage import func1, Class1, func2
    
    # 这样只会导入__all__中定义的内容
    from mypackage import *
    

    2.2.4 相对导入

    在包内部,可以使用相对导入:

    # mypackage/module1.py
    from .module2 import func2  # 导入同一包中的module2
    from ..subpackage import module3  # 导入父包的subpackage
    

    相对导入语法:

    • . : 当前包
    • .. : 父包
    • ... : 祖父包

    2.2.5 模块的__name__属性

    每个模块都有一个__name__属性:

    # mymodule.py
    
    def main():
        print("This is the main function")
    
    
    if __name__ == "__main__":
        print("This module is being run directly")
        main()
    else:
        print("This module is being imported")
    

    运行效果:

    # 直接运行模块
    $ python mymodule.py
    This module is being run directly
    This is the main function
    
    # 导入模块
    $ python -c "import mymodule"
    This module is being imported
    

    2.3 装饰器(Decorator)

    2.3.1 函数装饰器基础

    什么是装饰器?

    装饰器是一个接受函数作为参数,并返回一个新函数的函数。它可以在不修改原函数代码的情况下,为函数添加额外的功能。

    基本概念:

    # 简单的装饰器示例
    def my_decorator(func):
        def wrapper():
            print("Before function call")
            func()
            print("After function call")
        return wrapper
    
    
    @my_decorator
    def say_hello():
        print("Hello!")
    
    
    # 调用函数
    say_hello()
    # 输出:
    # Before function call
    # Hello!
    # After function call
    
    
    # 等价于:
    # say_hello = my_decorator(say_hello)
    # say_hello()
    

    2.3.2 装饰带参数的函数

    def my_decorator(func):
        def wrapper(*args, **kwargs):
            print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
            result = func(*args, **kwargs)
            print(f"{func.__name__} returned: {result}")
            return result
        return wrapper
    
    
    @my_decorator
    def add(a, b):
        return a + b
    
    
    @my_decorator
    def greet(name, greeting="Hello"):
        return f"{greeting}, {name}!"
    
    
    print(add(3, 5))
    # 输出:
    # Calling add with args: (3, 5), kwargs: {}
    # add returned: 8
    # 8
    
    print(greet("Alice"))
    # 输出:
    # Calling greet with args: ('Alice',), kwargs: {'greeting': 'Hello'}
    # greet returned: Hello, Alice!
    # Hello, Alice!
    

    2.3.3 带参数的装饰器

    def repeat(times):
        def decorator(func):
            def wrapper(*args, **kwargs):
                results = []
                for _ in range(times):
                    result = func(*args, **kwargs)
                    results.append(result)
                return results
            return wrapper
        return decorator
    
    
    @repeat(3)
    def greet(name):
        return f"Hello, {name}!"
    
    
    print(greet("Bob"))
    # 输出:
    # ['Hello, Bob!', 'Hello, Bob!', 'Hello, Bob!']
    

    2.3.4 保留原函数的元数据

    使用functools.wraps装饰器保留原函数的元数据:

    import functools
    
    
    def my_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print(f"Calling {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    
    
    @my_decorator
    def add(a, b):
        """Add two numbers."""
        return a + b
    
    
    print(add.__name__)   # 输出: add
    print(add.__doc__)    # 输出: Add two numbers.
    
    
    # 如果不使用@functools.wraps,输出会是wrapper和None
    

    2.3.5 实用装饰器示例

    计时装饰器:

    import time
    import functools
    
    
    def timer(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
            return result
        return wrapper
    
    
    @timer
    def slow_function():
        time.sleep(1)
        return "Done!"
    
    
    print(slow_function())
    # 输出:
    # slow_function took 1.0012 seconds
    # Done!
    

    日志装饰器:

    import functools
    import logging
    
    logging.basicConfig(level=logging.INFO)
    
    
    def log(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            logging.info(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
            try:
                result = func(*args, **kwargs)
                logging.info(f"{func.__name__} returned {result}")
                return result
            except Exception as e:
                logging.error(f"{func.__name__} raised {e}")
                raise
        return wrapper
    
    
    @log
    def divide(a, b):
        return a / b
    
    
    print(divide(10, 2))
    # 输出:
    # INFO:root:Calling divide with args=(10, 2), kwargs={}
    # INFO:root:divide returned 5.0
    # 5.0
    
    print(divide(10, 0))
    # 输出:
    # INFO:root:Calling divide with args=(10, 0), kwargs={}
    # ERROR:root:divide raised division by zero
    # ZeroDivisionError: division by zero
    

    缓存装饰器:

    import functools
    
    
    def cache(func):
        cached_results = {}
        
        @functools.wraps(func)
        def wrapper(*args):
            if args not in cached_results:
                cached_results[args] = func(*args)
            return cached_results[args]
        return wrapper
    
    
    @cache
    def fibonacci(n):
        if n <= 1:
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)
    
    
    print(fibonacci(10))  # 输出: 55
    print(fibonacci(10))  # 第二次调用会从缓存中读取
    

    注意: Python内置了@functools.lru_cache装饰器,功能更强大:

    import functools
    
    
    @functools.lru_cache(maxsize=None)
    def fibonacci(n):
        if n <= 1:
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)
    

    2.3.6 类装饰器

    class CountCalls:
        def __init__(self, func):
            self.func = func
            self.count = 0
        
        def __call__(self, *args, **kwargs):
            self.count += 1
            print(f"Call {self.count} of {self.func.__name__}")
            return self.func(*args, **kwargs)
    
    
    @CountCalls
    def say_hello():
        print("Hello!")
    
    
    say_hello()  # 输出: Call 1 of say_hello
    say_hello()  # 输出: Call 2 of say_hello
    say_hello()  # 输出: Call 3 of say_hello
    

    2.4 生成器与迭代器

    2.4.1 迭代器(Iterator)

    什么是迭代器?

    迭代器是实现了__iter__()__next__()方法的对象,可以用于遍历序列。

    创建迭代器:

    class MyIterator:
        def __init__(self, data):
            self.data = data
            self.index = 0
        
        def __iter__(self):
            return self
        
        def __next__(self):
            if self.index < len(self.data):
                result = self.data[self.index]
                self.index += 1
                return result
            else:
                raise StopIteration
    
    
    # 使用迭代器
    my_iter = MyIterator([1, 2, 3, 4, 5])
    
    for item in my_iter:
        print(item)
    # 输出:
    # 1
    # 2
    # 3
    # 4
    # 5
    
    
    # 手动使用迭代器
    my_iter = MyIterator([1, 2, 3])
    print(next(my_iter))  # 输出: 1
    print(next(my_iter))  # 输出: 2
    print(next(my_iter))  # 输出: 3
    # print(next(my_iter))  # 抛出 StopIteration
    

    2.4.2 生成器(Generator)

    什么是生成器?

    生成器是一种特殊的迭代器,使用yield语句来生成值,而不是一次性返回所有结果。

    创建生成器:

    # 方法1:使用yield关键字
    def my_generator(n):
        for i in range(n):
            yield i
    
    
    # 使用生成器
    gen = my_generator(5)
    
    print(next(gen))  # 输出: 0
    print(next(gen))  # 输出: 1
    print(next(gen))  # 输出: 2
    
    # 在for循环中使用
    for num in my_generator(5):
        print(num)
    # 输出:
    # 0
    # 1
    # 2
    # 3
    # 4
    
    
    # 方法2:生成器表达式
    gen_expr = (x ** 2 for x in range(5))
    
    for num in gen_expr:
        print(num)
    # 输出:
    # 0
    # 1
    # 4
    # 9
    # 16
    

    2.4.3 生成器的优势

    1. 内存效率:

    # 列表方式(占用大量内存)
    def squares_list(n):
        return [x ** 2 for x in range(n)]
    
    # 生成器方式(内存友好)
    def squares_generator(n):
        for x in range(n):
            yield x ** 2
    
    
    # 对比
    import sys
    
    # 列表方式
    squares = squares_list(1000000)
    print(f"List size: {sys.getsizeof(squares)} bytes")  # 约8MB
    
    # 生成器方式
    gen = squares_generator(1000000)
    print(f"Generator size: {sys.getsizeof(gen)} bytes")  # 约200字节
    

    2. 惰性求值:

    def infinite_counter():
        n = 0
        while True:
            yield n
            n += 1
    
    
    # 无限序列(只在需要时才计算)
    counter = infinite_counter()
    
    print(next(counter))  # 输出: 0
    print(next(counter))  # 输出: 1
    print(next(counter))  # 输出: 2
    # 可以无限继续...
    

    2.4.4 生成器的高级用法

    发送值到生成器:

    def accumulator():
        total = 0
        while True:
            value = yield total
            if value is not None:
                total += value
    
    
    acc = accumulator()
    next(acc)  # 启动生成器
    
    print(acc.send(10))  # 输出: 10
    print(acc.send(20))  # 输出: 30
    print(acc.send(30))  # 输出: 60
    

    生成器委托:

    def sub_generator():
        yield "Sub 1"
        yield "Sub 2"
        yield "Sub 3"
    
    
    def main_generator():
        yield "Main 1"
        yield from sub_generator()  # 委托给子生成器
        yield "Main 2"
    
    
    for item in main_generator():
        print(item)
    # 输出:
    # Main 1
    # Sub 1
    # Sub 2
    # Sub 3
    # Main 2
    

    2.4.5 实用生成器示例

    斐波那契数列:

    def fibonacci():
        a, b = 0, 1
        while True:
            yield a
            a, b = b, a + b
    
    
    # 获取前10个斐波那契数
    fib = fibonacci()
    fibonacci_numbers = [next(fib) for _ in range(10)]
    print(fibonacci_numbers)
    # 输出: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    

    读取大文件:

    def read_large_file(file_path):
        with open(file_path, 'r') as file:
            for line in file:
                yield line.strip()
    
    
    # 逐行处理大文件,不占用过多内存
    for line in read_large_file('large_file.txt'):
        process(line)  # 处理每一行
    

    生成器链:

    def numbers():
        for i in range(10):
            yield i
    
    
    def even_numbers(numbers):
        for num in numbers:
            if num % 2 == 0:
                yield num
    
    
    def squared(numbers):
        for num in numbers:
            yield num ** 2
    
    
    # 链式调用
    result = squared(even_numbers(numbers()))
    
    for num in result:
        print(num)
    # 输出:
    # 0
    # 4
    # 16
    # 36
    # 64
    

    2.5 异常处理

    2.5.1 异常基础

    什么是异常?

    异常是程序运行时发生的错误,Python使用异常对象来表示错误。

    基本语法:

    try:
        # 可能引发异常的代码
        result = 10 / 0
    except ZeroDivisionError as e:
        # 处理特定异常
        print(f"Error: {e}")
    except Exception as e:
        # 处理其他所有异常
        print(f"Unexpected error: {e}")
    else:
        # 没有异常时执行
        print("No errors occurred")
    finally:
        # 无论是否有异常都会执行
        print("This always runs")
    
    
    # 输出:
    # Error: division by zero
    # This always runs
    

    2.5.2 常见异常类型

    # ZeroDivisionError: 除数为零
    try:
        result = 10 / 0
    except ZeroDivisionError:
        print("Cannot divide by zero")
    
    
    # TypeError: 类型错误
    try:
        result = "10" + 5
    except TypeError as e:
        print(f"TypeError: {e}")
    
    
    # ValueError: 值错误
    try:
        number = int("abc")
    except ValueError as e:
        print(f"ValueError: {e}")
    
    
    # IndexError: 索引越界
    try:
        items = [1, 2, 3]
        print(items[10])
    except IndexError as e:
        print(f"IndexError: {e}")
    
    
    # KeyError: 键不存在
    try:
        data = {"name": "Alice"}
        print(data["age"])
    except KeyError as e:
        print(f"KeyError: {e}")
    
    
    # FileNotFoundError: 文件不存在
    try:
        with open("nonexistent.txt") as f:
            content = f.read()
    except FileNotFoundError as e:
        print(f"FileNotFoundError: {e}")
    
    
    # AttributeError: 属性不存在
    try:
        text = "hello"
        text.append("world")
    except AttributeError as e:
        print(f"AttributeError: {e}")
    

    2.5.3 自定义异常

    # 自定义异常类
    class InvalidAgeError(Exception):
        """当年龄无效时抛出"""
        pass
    
    
    class NegativeValueError(Exception):
        """当值为负数时抛出"""
        pass
    
    
    def set_age(age):
        if age < 0:
            raise NegativeValueError("Age cannot be negative")
        if age > 150:
            raise InvalidAgeError("Age cannot be greater than 150")
        return age
    
    
    # 使用自定义异常
    try:
        age = set_age(-5)
    except NegativeValueError as e:
        print(f"NegativeValueError: {e}")
    except InvalidAgeError as e:
        print(f"InvalidAgeError: {e}")
    else:
        print(f"Age set to: {age}")
    
    
    # 输出: NegativeValueError: Age cannot be negative
    

    2.5.4 异常处理最佳实践

    1. 具体捕获异常:

    # 好的做法:捕获具体异常
    try:
        result = 10 / 0
    except ZeroDivisionError:
        print("Cannot divide by zero")
    
    
    # 不好的做法:捕获所有异常
    try:
        result = 10 / 0
    except:  # 不要这样做!
        print("An error occurred")
    

    2. 提供有用的错误信息:

    def divide(a, b):
        try:
            return a / b
        except ZeroDivisionError:
            raise ValueError(f"Cannot divide {a} by {b}") from None
    
    
    try:
        result = divide(10, 0)
    except ValueError as e:
        print(f"Error: {e}")
    

    3. 使用finally清理资源:

    def process_file(filename):
        file = None
        try:
            file = open(filename, 'r')
            content = file.read()
            return content
        except FileNotFoundError:
            print(f"File {filename} not found")
            return None
        finally:
            if file:
                file.close()
                print("File closed")
    
    
    # 更好的方式:使用with语句
    def process_file(filename):
        try:
            with open(filename, 'r') as file:
                return file.read()
        except FileNotFoundError:
            print(f"File {filename} not found")
            return None
    

    4. 异常链:

    def read_config(filename):
        try:
            with open(filename) as f:
                return f.read()
        except FileNotFoundError as e:
            raise RuntimeError(f"Configuration file {filename} not found") from e
    
    
    try:
        config = read_config("config.txt")
    except RuntimeError as e:
        print(f"Error: {e}")
        print(f"Caused by: {e.__cause__}")
    

    2.5.5 上下文管理器与异常处理

    结合上下文管理器处理异常:

    from contextlib import contextmanager
    
    
    @contextmanager
    def error_handler(error_message):
        try:
            yield
        except Exception as e:
            print(f"{error_message}: {e}")
            raise
    
    
    # 使用
    with error_handler("Failed to process data"):
        data = [1, 2, 3]
        print(data[10])  # IndexError
    

    2.6 上下文管理器

    2.6.1 什么是上下文管理器?

    上下文管理器是一个对象,它定义了进入和退出上下文时应该执行的代码,主要用于资源管理(如文件、网络连接、锁等)。


    2.6.2 使用with语句

    基本用法:

    # 文件操作
    with open('example.txt', 'w') as file:
        file.write('Hello, World!')
    # 文件自动关闭,即使发生异常
    
    
    # 线程锁
    import threading
    
    lock = threading.Lock()
    
    with lock:
        # 临界区代码
        print("Critical section")
    # 锁自动释放
    
    
    # 数据库连接(示例)
    # with database.connection() as conn:
    #     cursor = conn.cursor()
    #     cursor.execute("SELECT * FROM users")
    # # 连接自动关闭
    

    2.6.3 创建上下文管理器

    方法1:使用类实现__enter____exit__方法:

    class FileManager:
        def __init__(self, filename, mode):
            self.filename = filename
            self.mode = mode
            self.file = None
        
        def __enter__(self):
            """进入上下文时调用"""
            self.file = open(self.filename, self.mode)
            return self.file
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            """退出上下文时调用"""
            if self.file:
                self.file.close()
            # 返回False会传播异常,返回True会抑制异常
            if exc_type is not None:
                print(f"An exception occurred: {exc_val}")
            return False
    
    
    # 使用
    with FileManager('example.txt', 'w') as f:
        f.write('Hello, Context Manager!')
        # f.write(None)  # 这会引发异常
    
    print("File operations completed")
    

    方法2:使用@contextmanager装饰器:

    from contextlib import contextmanager
    
    
    @contextmanager
    def file_manager(filename, mode):
        file = open(filename, mode)
        try:
            yield file
        finally:
            file.close()
    
    
    # 使用
    with file_manager('example.txt', 'w') as f:
        f.write('Hello from contextmanager!')
    

    2.6.4 实用上下文管理器示例

    计时上下文管理器:

    import time
    from contextlib import contextmanager
    
    
    @contextmanager
    def timer(name):
        start = time.time()
        yield
        elapsed = time.time() - start
        print(f"{name} took {elapsed:.4f} seconds")
    
    
    # 使用
    with timer("Data processing"):
        # 模拟耗时操作
        time.sleep(1)
        data = [i ** 2 for i in range(100000)]
    
    # 输出: Data processing took 1.0234 seconds
    

    临时修改配置:

    from contextlib import contextmanager
    
    
    @contextmanager
    def temporary_setting(obj, attr, value):
        """临时修改对象属性"""
        old_value = getattr(obj, attr)
        setattr(obj, attr, value)
        try:
            yield
        finally:
            setattr(obj, attr, old_value)
    
    
    # 使用
    class Config:
        debug = False
    
    
    config = Config()
    
    print(f"Debug mode: {config.debug}")  # 输出: Debug mode: False
    
    with temporary_setting(config, 'debug', True):
        print(f"Debug mode: {config.debug}")  # 输出: Debug mode: True
        # 在这个块中debug为True
    
    print(f"Debug mode: {config.debug}")  # 输出: Debug mode: False
    

    数据库事务:

    from contextlib import contextmanager
    
    
    @contextmanager
    def database_transaction(connection):
        """模拟数据库事务"""
        cursor = connection.cursor()
        try:
            yield cursor
            connection.commit()  # 提交事务
            print("Transaction committed")
        except Exception as e:
            connection.rollback()  # 回滚事务
            print(f"Transaction rolled back: {e}")
            raise
        finally:
            cursor.close()
    
    
    # 使用示例(伪代码)
    # with database_transaction(conn) as cursor:
    #     cursor.execute("INSERT INTO users VALUES (?, ?)", (1, 'Alice'))
    #     cursor.execute("INSERT INTO users VALUES (?, ?)", (2, 'Bob'))
    

    抑制异常:

    from contextlib import suppress
    
    
    # 抑制特定异常
    with suppress(FileNotFoundError):
        with open('nonexistent.txt') as f:
            content = f.read()
    
    print("Continuing execution...")
    # 输出: Continuing execution...
    # 程序不会因为FileNotFoundError而停止
    

    2.6.5 嵌套上下文管理器

    from contextlib import contextmanager
    
    
    @contextmanager
    def context_a():
        print("Entering A")
        yield
        print("Exiting A")
    
    
    @contextmanager
    def context_b():
        print("Entering B")
        yield
        print("Exiting B")
    
    
    # 嵌套使用
    with context_a():
        with context_b():
            print("In both contexts")
    
    # 输出:
    # Entering A
    # Entering B
    # In both contexts
    # Exiting B
    # Exiting A
    
    
    # 使用ExitStack管理多个上下文
    from contextlib import ExitStack
    
    
    with ExitStack() as stack:
        file1 = stack.enter_context(open('file1.txt', 'w'))
        file2 = stack.enter_context(open('file2.txt', 'w'))
        # 使用file1和file2
        file1.write("Content 1")
        file2.write("Content 2")
    # 所有文件都会正确关闭
    

    2.7 综合示例

    2.7.1 OneForAll中的模块基类

    在OneForAll项目中,common/module.py定义了所有收集模块的基类,使用了我们学到的许多概念:

    import threading
    import time
    import requests
    from functools import wraps
    
    
    class Module:
        """OneForAll模块基类"""
        
        def __init__(self):
            self.module = 'Module'
            self.source = 'BaseModule'
            self.header = dict()
            self.proxy = None
            self.timeout = 30
            self.domain = str()
            self.subdomains = set()
            self.start = time.time()
            self.end = None
        
        def begin(self):
            """开始日志"""
            print(f"Starting {self.source} module")
        
        def finish(self):
            """完成日志"""
            self.end = time.time()
            elapse = round(self.end - self.start, 1)
            print(f"Finished {self.source} module in {elapse} seconds")
        
        def get(self, url, check=True, **kwargs):
            """HTTP GET请求"""
            try:
                response = requests.get(url, headers=self.header,
                                       proxies=self.proxy,
                                       timeout=self.timeout, **kwargs)
                if check and response.status_code == 200:
                    return response
                return response
            except Exception as e:
                print(f"Request error: {e}")
                return None
        
        def save_db(self):
            """保存结果到数据库"""
            # 实现数据库保存逻辑
            pass
    
    
    # 使用装饰器添加日志功能
    def log_execution(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            self.begin()
            result = func(self, *args, **kwargs)
            self.finish()
            return result
        return wrapper
    
    
    # 自定义异常
    class ModuleError(Exception):
        """模块异常基类"""
        pass
    
    
    class APIError(ModuleError):
        """API调用异常"""
        pass
    
    
    # 使用示例
    class MyModule(Module):
        def __init__(self):
            super().__init__()
            self.module = 'MyModule'
            self.source = 'MySource'
        
        @log_execution
        def run(self, domain):
            """运行模块"""
            self.domain = domain
            # 模拟收集子域名
            self.subdomains = {
                f"www.{domain}",
                f"mail.{domain}",
                f"api.{domain}"
            }
            self.save_db()
            return self.subdomains
    
    
    # 使用模块
    module = MyModule()
    subdomains = module.run("example.com")
    print(f"Found subdomains: {subdomains}")
    

    2.7.2 实用工具函数

    1. 带重试的HTTP请求:

    import time
    from functools import wraps
    
    
    def retry(max_attempts=3, delay=1):
        """重试装饰器"""
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                attempts = 0
                while attempts < max_attempts:
                    try:
                        return func(*args, **kwargs)
                    except Exception as e:
                        attempts += 1
                        if attempts == max_attempts:
                            raise
                        time.sleep(delay)
                return None
            return wrapper
        return decorator
    
    
    @retry(max_attempts=3, delay=2)
    def fetch_data(url):
        """获取数据"""
        import requests
        response = requests.get(url)
        response.raise_for_status()
        return response.json()
    
    
    # 使用
    try:
        data = fetch_data("https://api.example.com/data")
    except Exception as e:
        print(f"Failed to fetch data: {e}")
    

    2. 缓存装饰器:

    import functools
    import hashlib
    import pickle
    import os
    
    
    def disk_cache(cache_dir="cache"):
        """磁盘缓存装饰器"""
        os.makedirs(cache_dir, exist_ok=True)
        
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                # 生成缓存键
                key = hashlib.md5(
                    pickle.dumps((args, kwargs))
                ).hexdigest()
                cache_file = os.path.join(cache_dir, f"{key}.cache")
                
                # 尝试从缓存读取
                if os.path.exists(cache_file):
                    with open(cache_file, 'rb') as f:
                        return pickle.load(f)
                
                # 执行函数并缓存结果
                result = func(*args, **kwargs)
                with open(cache_file, 'wb') as f:
                    pickle.dump(result, f)
                
                return result
            return wrapper
        return decorator
    
    
    @disk_cache()
    def expensive_computation(n):
        """耗时的计算"""
        print(f"Computing {n}...")
        return sum(i ** 2 for i in range(n))
    
    
    # 第一次调用会执行计算
    print(expensive_computation(1000000))
    
    # 第二次调用从缓存读取
    print(expensive_computation(1000000))
    

    3. 上下文管理器管理多个资源:

    from contextlib import contextmanager, ExitStack
    
    
    @contextmanager
    def resource_manager():
        """管理多个资源"""
        with ExitStack() as stack:
            # 可以动态添加多个上下文管理器
            resources = []
            for i in range(3):
                resource = stack.enter_context(open(f"file_{i}.txt", "w"))
                resources.append(resource)
            
            yield resources
        # 所有资源自动关闭
    
    
    # 使用
    with resource_manager() as files:
        for i, file in enumerate(files):
            file.write(f"Content of file {i}")
    

    2.8 实践任务

    任务1:实现一个简单的类层次结构

    目标: 创建一个动物类层次结构,演示继承、多态和封装。

    要求:

    1. 创建基类Animal,包含属性nameage,方法speak()eat()
    2. 创建子类DogCat,重写speak()方法
    3. 添加私有属性和方法,演示封装
    4. 创建多个动物对象,使用多态调用speak()方法

    代码框架:

    # 在这里实现你的代码
    
    class Animal:
        pass
    
    
    class Dog(Animal):
        pass
    
    
    class Cat(Animal):
        pass
    
    
    # 测试代码
    animals = [Dog("Buddy", 3), Cat("Whiskers", 2)]
    for animal in animals:
        print(animal.speak())
    

    任务2:创建和使用装饰器

    目标: 实现至少3个实用的装饰器。

    要求:

    1. 创建一个计时装饰器,测量函数执行时间
    2. 创建一个日志装饰器,记录函数调用信息
    3. 创建一个缓存装饰器,缓存函数结果
    4. 应用这些装饰器到示例函数并测试

    代码框架:

    # 在这里实现你的装饰器
    
    def timer(func):
        pass
    
    
    def logger(func):
        pass
    
    
    def cache(func):
        pass
    
    
    # 测试装饰器
    @timer
    @logger
    @cache
    def fibonacci(n):
        if n <= 1:
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)
    
    print(fibonacci(10))
    

    任务3:实现生成器和迭代器

    目标: 创建一个生成器和迭代器来处理数据。

    要求:

    1. 创建一个生成器,生成斐波那契数列
    2. 创建一个迭代器类,实现倒序遍历
    3. 使用生成器表达式处理数据
    4. 比较生成器和列表的内存使用

    代码框架:

    # 在这里实现你的代码
    
    def fibonacci_generator():
        pass
    
    
    class ReverseIterator:
        pass
    
    
    # 测试代码
    fib = fibonacci_generator()
    for i in range(10):
        print(next(fib))
    
    data = [1, 2, 3, 4, 5]
    rev_iter = ReverseIterator(data)
    for item in rev_iter:
        print(item)
    

    任务4:异常处理练习

    目标: 实现一个函数,包含完整的异常处理。

    要求:

    1. 创建一个函数,读取和处理文件
    2. 处理文件不存在、权限错误、编码错误等异常
    3. 使用自定义异常
    4. 使用finally确保资源释放
    5. 提供有用的错误信息

    代码框架:

    # 在这里实现你的代码
    
    class FileProcessingError(Exception):
        pass
    
    
    def process_file(filename):
        try:
            # 实现文件处理逻辑
            pass
        except Exception as e:
            # 处理异常
            pass
        finally:
            # 清理资源
            pass
    
    
    # 测试代码
    try:
        result = process_file("example.txt")
        print(result)
    except FileProcessingError as e:
        print(f"Error: {e}")
    

    任务5:创建上下文管理器

    目标: 实现至少2个实用的上下文管理器。

    要求:

    1. 创建一个计时上下文管理器,测量代码块执行时间
    2. 创建一个临时目录上下文管理器,自动创建和清理临时目录
    3. 使用@contextmanager装饰器实现
    4. 测试嵌套使用上下文管理器

    代码框架:

    # 在这里实现你的上下文管理器
    
    from contextlib import contextmanager
    
    
    @contextmanager
    def timer():
        pass
    
    
    @contextmanager
    def temp_directory():
        pass
    
    
    # 测试代码
    with timer():
        with temp_directory() as temp_dir:
            print(f"Working in {temp_dir}")
            # 在临时目录中工作
    

    2.9 本课总结

    本课重点内容回顾

    1. 面向对象编程

    • 类与对象的概念
    • 继承:代码复用和扩展
    • 多态:相同方法不同响应
    • 封装:隐藏实现细节
    • 特殊方法:自定义对象行为

    2. 模块和包

    • 模块:Python文件作为模块
    • 包:包含__init__.py的目录
    • 导入机制:import、from...import
    • __init__.py的作用
    • 相对导入和绝对导入

    3. 装饰器

    • 函数装饰器:不修改原函数添加功能
    • 带参数的装饰器
    • @functools.wraps保留元数据
    • 类装饰器
    • 实用装饰器:计时、日志、缓存

    4. 生成器与迭代器

    • 迭代器:__iter____next__
    • 生成器:yield关键字
    • 生成器表达式
    • 内存效率和惰性求值
    • 高级用法:send、yield from

    5. 异常处理

    • try-except-else-finally结构
    • 常见异常类型
    • 自定义异常
    • 异常链
    • 最佳实践

    6. 上下文管理器

    • __enter____exit__方法
    • @contextmanager装饰器
    • with语句的使用
    • 实用示例:计时、临时修改、事务

    下节课预告

    第3课:网络编程基础

    • HTTP协议详解
    • DNS协议原理
    • requests库使用
    • 代理和认证
    • 网络超时处理
    • 实践:实现HTTP客户端

    课后思考

    1. 为什么OneForAll要使用模块化的设计?
    2. 装饰器在OneForAll中有哪些应用场景?
    3. 生成器在处理大量子域名时有什么优势?
    4. 如何设计一个良好的异常处理策略?
    5. 上下文管理器在资源管理中有什么作用?

    推荐阅读


    2.10 附录

    附录A:Python特殊方法速查表

    class MyClass:
        # 对象创建和销毁
        def __new__(cls, *args, **kwargs): pass
        def __init__(self, *args, **kwargs): pass
        def __del__(self): pass
        
        # 字符串表示
        def __str__(self): pass
        def __repr__(self): pass
        
        # 比较操作
        def __eq__(self, other): pass
        def __ne__(self, other): pass
        def __lt__(self, other): pass
        def __gt__(self, other): pass
        def __le__(self, other): pass
        def __ge__(self, other): pass
        
        # 算术运算
        def __add__(self, other): pass
        def __sub__(self, other): pass
        def __mul__(self, other): pass
        def __truediv__(self, other): pass
        def __floordiv__(self, other): pass
        def __mod__(self, other): pass
        def __pow__(self, other): pass
        
        # 容器类型
        def __len__(self): pass
        def __getitem__(self, key): pass
        def __setitem__(self, key, value): pass
        def __delitem__(self, key): pass
        def __contains__(self, item): pass
        def __iter__(self): pass
        
        # 可调用对象
        def __call__(self, *args, **kwargs): pass
        
        # 上下文管理器
        def __enter__(self): pass
        def __exit__(self, exc_type, exc_val, exc_tb): pass
    

    附录B:常用装饰器模板

    # 1. 计时装饰器
    import time
    import functools
    
    
    def timer(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"{func.__name__} took {end - start:.4f} seconds")
            return result
        return wrapper
    
    
    # 2. 日志装饰器
    import logging
    import functools
    
    
    logging.basicConfig(level=logging.INFO)
    
    
    def logger(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            logging.info(f"Calling {func.__name__}")
            try:
                result = func(*args, **kwargs)
                logging.info(f"{func.__name__} succeeded")
                return result
            except Exception as e:
                logging.error(f"{func.__name__} failed: {e}")
                raise
        return wrapper
    
    
    # 3. 重试装饰器
    import time
    import functools
    
    
    def retry(max_attempts=3, delay=1):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                attempts = 0
                while attempts < max_attempts:
                    try:
                        return func(*args, **kwargs)
                    except Exception as e:
                        attempts += 1
                        if attempts == max_attempts:
                            raise
                        time.sleep(delay)
            return wrapper
        return decorator
    
    
    # 4. 单例装饰器
    def singleton(cls):
        instances = {}
        
        @functools.wraps(cls)
        def get_instance(*args, **kwargs):
            if cls not in instances:
                instances[cls] = cls(*args, **kwargs)
            return instances[cls]
        
        return get_instance
    
    
    # 5. 缓存装饰器
    import functools
    
    
    @functools.lru_cache(maxsize=None)
    def cached_function(arg):
        # 函数实现
        pass
    

    附录C:异常处理最佳实践清单


    附录D:学习检查清单


    恭喜你完成了第2课的学习!🎉

    现在你已经掌握了Python的高级特性,包括面向对象编程、模块化设计、装饰器、生成器、异常处理和上下文管理器。这些都是开发OneForAll这样复杂项目所必需的技能。

    在下一课中,我们将学习网络编程基础,了解HTTP和DNS协议,为后续学习OneForAll的网络请求和DNS解析功能做好准备。

    记住: 理论知识需要通过实践来巩固,一定要动手完成所有的实践任务!

    继续加油,我们下节课见!💪

    posted @ 2026-04-20 16:52  羽弥YUMI  阅读(9)  评论(0)    收藏  举报