Series Article

Python 基础课 Day5:面向对象、类型提示与 HTTP 请求

Python 基础课:面向对象、类型提示与 HTTP 请求

学习目标

今天结束后,你能够:

  • 理解类和对象的概念
  • 定义类、创建实例、使用方法
  • 使用继承和多态构建代码结构
  • 添加类型提示提高代码可读性
  • 使用 dataclass 简化数据类
  • 使用 Pydantic 进行数据验证
  • 发送 HTTP 请求调用 API
  • 从环境变量读取配置

面向对象编程基础

为什么需要面向对象

函数式编程处理简单任务很方便,但当数据和操作复杂时,面向对象能更好地组织代码。

函数式:

# 管理用户
users = []

def add_user(name, email):
    users.append({"name": name, "email": email})

def get_user(name):
    for user in users:
        if user["name"] == name:
            return user

面向对象:

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

# 数据和操作封装在一起
user = User("Alice", "alice@example.com")
print(user.name)

类和对象

类(Class):模板,定义了对象的属性和方法 对象(Object):类的实例,实际存在的数据

类比:

  • 类 = 图纸(建筑设计图)
  • 对象 = 建筑物(根据图纸建造的房子)

定义类

基本语法:

class ClassName:
    """类的说明文档"""
    
    def __init__(self, param1, param2):
        """初始化方法(构造函数)"""
        self.attr1 = param1
        self.attr2 = param2
    
    def method1(self):
        """实例方法"""
        return self.attr1

示例:

class Message:
    """消息类"""
    
    def __init__(self, role, content):
        self.role = role
        self.content = content
    
    def format(self):
        """格式化输出"""
        return f"[{self.role}] {self.content}"
    
    def token_count(self):
        """估算 token 数"""
        return len(self.content) // 4

# 创建对象
msg = Message("user", "你好")
print(msg.format())  # [user] 你好
print(msg.token_count())  # 0

__init__ 和 self

__init__:初始化方法,创建对象时自动调用 self:指向对象本身,类似其他语言的 this

class AIModel:
    def __init__(self, name, temperature=0.7):
        self.name = name
        self.temperature = temperature
    
    def describe(self):
        # self 指向当前对象
        return f"模型 {self.name},温度 {self.temperature}"

# 创建对象
model1 = AIModel("gpt-4")
model2 = AIModel("claude-3", 0.9)

print(model1.describe())  # 模型 gpt-4,温度 0.7
print(model2.describe())  # 模型 claude-3,温度 0.9

实例方法、类方法、静态方法

实例方法:最常用,访问实例属性

class Calculator:
    def add(self, a, b):
        return a + b

类方法:访问类属性,用 @classmethod 装饰

class AIModel:
    model_count = 0  # 类属性
    
    def __init__(self, name):
        self.name = name
        AIModel.model_count += 1
    
    @classmethod
    def get_count(cls):
        return cls.model_count

# 使用
model1 = AIModel("gpt-4")
model2 = AIModel("claude-3")
print(AIModel.get_count())  # 2

静态方法:不访问类或实例属性,用 @staticmethod 装饰

class TokenUtils:
    @staticmethod
    def estimate_tokens(text):
        return len(text) // 4
    
    @staticmethod
    def calculate_cost(tokens, price_per_1k):
        return (tokens / 1000) * price_per_1k

# 使用(不需要创建对象)
tokens = TokenUtils.estimate_tokens("Hello World")
cost = TokenUtils.calculate_cost(tokens, 0.03)

AI 场景示例

class ChatMessage:
    """对话消息类"""
    
    def __init__(self, role, content):
        self.role = role
        self.content = content
    
    def to_dict(self):
        """转为字典(API 格式)"""
        return {"role": self.role, "content": self.content}
    
    def token_count(self):
        """估算 token 数"""
        return len(self.content) // 4

class ChatSession:
    """对话会话类"""
    
    def __init__(self, system_prompt=""):
        self.messages = []
        if system_prompt:
            self.add_message("system", system_prompt)
    
    def add_message(self, role, content):
        """添加消息"""
        msg = ChatMessage(role, content)
        self.messages.append(msg)
    
    def get_messages(self):
        """获取所有消息(字典格式)"""
        return [msg.to_dict() for msg in self.messages]
    
    def total_tokens(self):
        """计算总 token 数"""
        return sum(msg.token_count() for msg in self.messages)

# 使用
session = ChatSession("你是一个Python助手")
session.add_message("user", "什么是类?")
session.add_message("assistant", "类是面向对象编程的基础")

print(f"消息数: {len(session.messages)}")
print(f"总 tokens: {session.total_tokens()}")
print(session.get_messages())

输出:

消息数: 3
总 tokens: 9
[{'role': 'system', 'content': '你是一个Python助手'}, {'role': 'user', 'content': '什么是类?'}, {'role': 'assistant', 'content': '类是面向对象编程的基础'}]

课堂练习

模型配置类

创建一个 AIModelConfig 类,管理模型配置。

参考答案:

class AIModelConfig:
    """AI 模型配置类"""
    
    def __init__(self, model="gpt-3.5-turbo", temperature=0.7, max_tokens=2000):
        self.model = model
        self.temperature = temperature
        self.max_tokens = max_tokens
    
    def validate(self):
        """验证配置"""
        if not (0 <= self.temperature <= 2):
            return False, "温度必须在 0-2 之间"
        if self.max_tokens <= 0:
            return False, "max_tokens 必须大于 0"
        return True, "配置合法"
    
    def to_dict(self):
        """转为字典"""
        return {
            "model": self.model,
            "temperature": self.temperature,
            "max_tokens": self.max_tokens
        }

# 测试
config = AIModelConfig("gpt-4", 0.7, 2000)
is_valid, message = config.validate()
print(f"验证结果: {message}")
print(f"配置: {config.to_dict()}")

输出:

验证结果: 配置合法
配置: {'model': 'gpt-4', 'temperature': 0.7, 'max_tokens': 2000}

继承与多态

继承

继承让子类复用父类的代码,实现代码重用。

基本语法:

class Parent:
    def method1(self):
        return "Parent method"

class Child(Parent):  # 继承 Parent
    def method2(self):
        return "Child method"

# 子类可以使用父类的方法
obj = Child()
print(obj.method1())  # Parent method
print(obj.method2())  # Child method

重写父类方法:

class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):  # 重写父类方法
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

dog = Dog()
cat = Cat()
print(dog.speak())  # Woof!
print(cat.speak())  # Meow!

调用父类方法:

class BaseModel:
    def __init__(self, name):
        self.name = name
    
    def describe(self):
        return f"模型: {self.name}"

class GPTModel(BaseModel):
    def __init__(self, name, version):
        super().__init__(name)  # 调用父类的 __init__
        self.version = version
    
    def describe(self):
        base_desc = super().describe()  # 调用父类的 describe
        return f"{base_desc}, 版本: {self.version}"

model = GPTModel("GPT", "4")
print(model.describe())  # 模型: GPT, 版本: 4

AI 场景示例

class BaseMessage:
    """基础消息类"""
    
    def __init__(self, content):
        self.content = content
    
    def token_count(self):
        return len(self.content) // 4
    
    def to_dict(self):
        raise NotImplementedError("子类必须实现 to_dict")

class UserMessage(BaseMessage):
    """用户消息"""
    
    def to_dict(self):
        return {"role": "user", "content": self.content}

class AssistantMessage(BaseMessage):
    """助手消息"""
    
    def __init__(self, content, model="gpt-4"):
        super().__init__(content)
        self.model = model
    
    def to_dict(self):
        return {
            "role": "assistant",
            "content": self.content,
            "model": self.model
        }

# 使用
user_msg = UserMessage("你好")
assistant_msg = AssistantMessage("你好!", "gpt-4")

print(user_msg.to_dict())
print(assistant_msg.to_dict())
print(f"用户消息 tokens: {user_msg.token_count()}")

封装:私有属性与 @property

封装是面向对象的核心特性,通过隐藏内部实现细节,只暴露必要的接口。

私有属性

Python 中使用下划线前缀表示私有:

  • _attribute:单下划线,约定为”内部使用”(弱私有)
  • __attribute:双下划线,名称改写机制(强私有)
class APIClient:
    def __init__(self, api_key):
        self._api_key = api_key      # 单下划线:约定为私有
        self.__secret = "secret123"   # 双下划线:强私有
    
    def get_key_preview(self):
        return self._api_key[:10] + "..."

client = APIClient("sk-1234567890abcdefghij")
print(client._api_key)           # 可以访问,但不建议
# print(client.__secret)         # AttributeError(无法直接访问)
print(client.get_key_preview())  # sk-1234567...

实际开发建议:

  • 使用单下划线 _ 表示私有(Python 社区约定)
  • 双下划线 __ 很少使用(除非真的需要防止子类覆盖)

@property 装饰器

@property 将方法转换为属性,提供优雅的访问接口。

基础用法:

class ChatSession:
    def __init__(self):
        self._messages = []
    
    @property
    def message_count(self):
        """消息数量(只读属性)"""
        return len(self._messages)
    
    def add_message(self, role, content):
        self._messages.append({"role": role, "content": content})

session = ChatSession()
session.add_message("user", "你好")
session.add_message("assistant", "你好!")

print(session.message_count)  # 2(像访问属性一样)
# session.message_count = 10  # AttributeError(只读,不能赋值)

getter 和 setter:

class ModelConfig:
    def __init__(self, temperature=0.7):
        self._temperature = temperature
    
    @property
    def temperature(self):
        """获取温度参数"""
        return self._temperature
    
    @temperature.setter
    def temperature(self, value):
        """设置温度参数(带验证)"""
        if not 0 <= value <= 2:
            raise ValueError("temperature 必须在 0-2 之间")
        self._temperature = value

config = ModelConfig()
print(config.temperature)  # 0.7

config.temperature = 0.9   # 通过 setter 设置
print(config.temperature)  # 0.9

# config.temperature = 3.0  # ValueError: temperature 必须在 0-2 之间

AI 场景示例

class APIClient:
    """API 客户端"""
    
    def __init__(self, api_key):
        self._api_key = api_key
        self._request_count = 0
        self._total_tokens = 0
    
    @property
    def api_key_preview(self):
        """显示 API Key 预览(隐藏真实值)"""
        if len(self._api_key) > 10:
            return self._api_key[:10] + "..." + self._api_key[-4:]
        return "***"
    
    @property
    def request_count(self):
        """请求次数(只读)"""
        return self._request_count
    
    @property
    def total_tokens(self):
        """总 token 数(只读)"""
        return self._total_tokens
    
    @property
    def average_tokens(self):
        """平均每次请求的 token 数(计算属性)"""
        if self._request_count == 0:
            return 0
        return self._total_tokens / self._request_count
    
    def call_api(self, prompt, tokens_used):
        """调用 API"""
        self._request_count += 1
        self._total_tokens += tokens_used
        return f"Response for: {prompt}"

# 使用
client = APIClient("sk-1234567890abcdefghijklmnop")
print(f"API Key: {client.api_key_preview}")  # sk-1234567...mnop

client.call_api("你好", 100)
client.call_api("天气", 150)

print(f"请求次数: {client.request_count}")      # 2
print(f"总tokens: {client.total_tokens}")       # 250
print(f"平均tokens: {client.average_tokens}")   # 125.0

使用场景

1. 只读属性

class Message:
    def __init__(self, content):
        self._content = content
        self._timestamp = datetime.now()
    
    @property
    def timestamp(self):
        """时间戳(只读)"""
        return self._timestamp
    
    @property
    def content_preview(self):
        """内容预览(只读)"""
        return self._content[:50] + "..." if len(self._content) > 50 else self._content

2. 计算属性

class TokenCounter:
    def __init__(self):
        self._input_tokens = 0
        self._output_tokens = 0
    
    @property
    def total_tokens(self):
        """总 tokens(计算得出)"""
        return self._input_tokens + self._output_tokens
    
    @property
    def cost(self):
        """费用(根据 tokens 计算)"""
        return (self._input_tokens * 0.002 + self._output_tokens * 0.004) / 1000

3. 属性验证

class ChatConfig:
    def __init__(self):
        self._max_tokens = 2000
    
    @property
    def max_tokens(self):
        return self._max_tokens
    
    @max_tokens.setter
    def max_tokens(self, value):
        if not isinstance(value, int):
            raise TypeError("max_tokens 必须是整数")
        if value <= 0:
            raise ValueError("max_tokens 必须大于 0")
        if value > 4000:
            raise ValueError("max_tokens 不能超过 4000")
        self._max_tokens = value

课堂练习

安全的 API 配置类

创建一个配置类,使用私有属性和 @property 保护敏感信息。

参考答案:

from datetime import datetime

class APIConfig:
    """API 配置类"""
    
    def __init__(self, api_key, model="gpt-3.5-turbo"):
        self._api_key = api_key
        self._model = model
        self._created_at = datetime.now()
    
    @property
    def api_key_masked(self):
        """返回隐藏的 API Key"""
        return "*" * (len(self._api_key) - 4) + self._api_key[-4:]
    
    @property
    def model(self):
        """模型名称"""
        return self._model
    
    @model.setter
    def model(self, value):
        """设置模型(带验证)"""
        allowed_models = ["gpt-3.5-turbo", "gpt-4", "claude-3"]
        if value not in allowed_models:
            raise ValueError(f"不支持的模型: {value}")
        self._model = value
    
    @property
    def age_seconds(self):
        """配置存在时长(秒)"""
        return (datetime.now() - self._created_at).total_seconds()

# 测试
config = APIConfig("sk-1234567890")
print(f"API Key: {config.api_key_masked}")  # **********7890
print(f"模型: {config.model}")               # gpt-3.5-turbo

config.model = "gpt-4"
print(f"新模型: {config.model}")             # gpt-4

# config.model = "unknown"  # ValueError: 不支持的模型

类型提示

类型提示让代码更易读、易维护,IDE 能提供更好的代码补全。

基础类型提示

def greet(name: str) -> str:
    return f"Hello, {name}!"

def add(a: int, b: int) -> int:
    return a + b

# 变量类型提示
age: int = 25
name: str = "Alice"
is_active: bool = True

复杂类型提示

from typing import List, Dict, Optional, Union

# 列表
def process_messages(messages: List[str]) -> int:
    return len(messages)

# 字典
def get_config() -> Dict[str, any]:
    return {"model": "gpt-4", "temperature": 0.7}

# 可选值
def find_user(name: str) -> Optional[Dict]:
    # 可能返回字典或 None
    return {"name": name} if name else None

# 多种类型
def format_value(value: Union[int, str, float]) -> str:
    return str(value)

AI 场景示例

from typing import List, Dict, Optional

class ChatAPI:
    """对话 API 类"""
    
    def __init__(self, api_key: str, model: str = "gpt-3.5-turbo"):
        self.api_key: str = api_key
        self.model: str = model
    
    def send_message(
        self,
        messages: List[Dict[str, str]],
        temperature: float = 0.7,
        max_tokens: int = 2000
    ) -> Optional[str]:
        """发送消息(模拟)"""
        if not messages:
            return None
        
        # 模拟 API 调用
        return "这是 AI 的回复"
    
    def count_tokens(self, text: str) -> int:
        """计算 token 数"""
        return len(text) // 4

# 使用
api = ChatAPI("sk-xxx", "gpt-4")
messages = [
    {"role": "user", "content": "你好"}
]
response = api.send_message(messages)
print(response)

dataclass

dataclass 是 Python 3.7+ 的特性,简化数据类的定义。

基本用法

传统方式:

class Message:
    def __init__(self, role, content):
        self.role = role
        self.content = content
    
    def __repr__(self):
        return f"Message(role={self.role}, content={self.content})"

使用 dataclass:

from dataclasses import dataclass

@dataclass
class Message:
    role: str
    content: str

# 自动生成 __init__、__repr__ 等方法
msg = Message("user", "你好")
print(msg)  # Message(role='user', content='你好')

默认值和字段

from dataclasses import dataclass, field
from typing import List

@dataclass
class ModelConfig:
    model: str = "gpt-3.5-turbo"
    temperature: float = 0.7
    max_tokens: int = 2000
    tags: List[str] = field(default_factory=list)  # 可变默认值

config = ModelConfig()
print(config)

AI 场景示例

from dataclasses import dataclass
from typing import List, Optional

@dataclass
class ChatMessage:
    role: str
    content: str
    
    def token_count(self) -> int:
        return len(self.content) // 4

@dataclass
class ChatRequest:
    model: str
    messages: List[ChatMessage]
    temperature: float = 0.7
    max_tokens: int = 2000
    stream: bool = False

@dataclass
class ChatResponse:
    content: str
    model: str
    tokens_used: int
    cost: float

# 使用
msg1 = ChatMessage("user", "你好")
msg2 = ChatMessage("assistant", "你好!")

request = ChatRequest(
    model="gpt-4",
    messages=[msg1, msg2]
)

print(request)

Pydantic

Pydantic 是强大的数据验证库,常用于 API 开发和配置管理。

安装

pip install pydantic

基本用法

from pydantic import BaseModel, Field

class User(BaseModel):
    name: str
    age: int
    email: str

# 自动验证类型
user = User(name="Alice", age=25, email="alice@example.com")
print(user)
print(user.model_dump())  # 转为字典

# 类型错误会抛出异常
try:
    invalid_user = User(name="Bob", age="not a number", email="bob@example.com")
except Exception as e:
    print(f"验证失败: {e}")

字段验证

from pydantic import BaseModel, Field

class ModelConfig(BaseModel):
    model: str = Field(..., description="模型名称")
    temperature: float = Field(0.7, ge=0, le=2, description="温度参数")
    max_tokens: int = Field(2000, gt=0, description="最大 token 数")
    
    class Config:
        # 配置
        validate_assignment = True  # 赋值时也验证

# 使用
config = ModelConfig(model="gpt-4", temperature=0.8)
print(config)

# 尝试设置非法值
try:
    config.temperature = 3.0  # 超出范围
except Exception as e:
    print(f"验证失败: {e}")

AI 场景示例

from pydantic import BaseModel, Field, validator
from typing import List, Optional

class ChatMessage(BaseModel):
    role: str = Field(..., pattern="^(system|user|assistant)$")
    content: str = Field(..., min_length=1)
    
    @validator('content')
    def content_not_empty(cls, v):
        if not v.strip():
            raise ValueError('内容不能为空')
        return v

class ChatRequest(BaseModel):
    model: str = Field(default="gpt-3.5-turbo")
    messages: List[ChatMessage] = Field(..., min_items=1)
    temperature: float = Field(default=0.7, ge=0, le=2)
    max_tokens: int = Field(default=2000, gt=0, le=4000)
    stream: bool = False

# 使用
try:
    request = ChatRequest(
        model="gpt-4",
        messages=[
            ChatMessage(role="user", content="你好")
        ],
        temperature=0.8
    )
    print(request.model_dump_json(indent=2))
except Exception as e:
    print(f"验证失败: {e}")

HTTP 请求与 API 调用

requests 库

requests 是最常用的 HTTP 库。

安装:

pip install requests

基本用法:

import requests

# GET 请求
response = requests.get("https://api.example.com/users")
print(response.status_code)  # 200
print(response.json())  # 解析 JSON

# POST 请求
data = {"name": "Alice", "email": "alice@example.com"}
response = requests.post("https://api.example.com/users", json=data)
print(response.json())

# 带请求头
headers = {"Authorization": "Bearer xxx"}
response = requests.get("https://api.example.com/protected", headers=headers)

异常处理

import requests

try:
    response = requests.get("https://api.example.com/data", timeout=5)
    response.raise_for_status()  # 状态码非 2xx 抛出异常
    data = response.json()
    print(data)
except requests.Timeout:
    print("请求超时")
except requests.HTTPError as e:
    print(f"HTTP 错误: {e}")
except requests.RequestException as e:
    print(f"请求失败: {e}")

AI API 调用示例

import requests
import os
from typing import List, Dict, Optional

class OpenAIClient:
    """OpenAI API 客户端"""
    
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv("OPENAI_API_KEY")
        self.base_url = "https://api.openai.com/v1"
    
    def chat_completion(
        self,
        messages: List[Dict[str, str]],
        model: str = "gpt-3.5-turbo",
        temperature: float = 0.7
    ) -> Optional[str]:
        """发送对话请求"""
        url = f"{self.base_url}/chat/completions"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        data = {
            "model": model,
            "messages": messages,
            "temperature": temperature
        }
        
        try:
            response = requests.post(url, headers=headers, json=data, timeout=30)
            response.raise_for_status()
            result = response.json()
            return result["choices"][0]["message"]["content"]
        except Exception as e:
            print(f"API 调用失败: {e}")
            return None

# 使用
client = OpenAIClient(api_key="sk-xxx")
messages = [
    {"role": "user", "content": "什么是 Python?"}
]
response = client.chat_completion(messages)
if response:
    print(response)

环境变量

环境变量用于存储敏感信息(如 API Key),避免硬编码。

使用 os.getenv

import os

# 读取环境变量
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
    print("API Key 未设置")
else:
    print(f"API Key: {api_key[:10]}...")  # 只显示前10位

使用 python-dotenv

python-dotenv 从 .env 文件加载环境变量。

安装:

pip install python-dotenv

创建 .env 文件:

OPENAI_API_KEY=sk-xxxxxxxxxxxx
MODEL=gpt-4
TEMPERATURE=0.7

加载环境变量:

from dotenv import load_dotenv
import os

# 加载 .env 文件
load_dotenv()

# 读取环境变量
api_key = os.getenv("OPENAI_API_KEY")
model = os.getenv("MODEL", "gpt-3.5-turbo")  # 提供默认值
temperature = float(os.getenv("TEMPERATURE", "0.7"))

print(f"模型: {model}")
print(f"温度: {temperature}")

AI 场景示例

from dotenv import load_dotenv
import os
from pydantic import BaseModel, Field

# 加载环境变量
load_dotenv()

class AppConfig(BaseModel):
    """应用配置"""
    api_key: str = Field(default_factory=lambda: os.getenv("OPENAI_API_KEY", ""))
    model: str = Field(default_factory=lambda: os.getenv("MODEL", "gpt-3.5-turbo"))
    temperature: float = Field(default_factory=lambda: float(os.getenv("TEMPERATURE", "0.7")))
    max_tokens: int = Field(default_factory=lambda: int(os.getenv("MAX_TOKENS", "2000")))
    
    def validate(self) -> bool:
        """验证配置"""
        if not self.api_key:
            print("错误: API Key 未设置")
            return False
        if not (0 <= self.temperature <= 2):
            print("错误: 温度参数无效")
            return False
        return True

# 使用
config = AppConfig()
if config.validate():
    print("配置加载成功")
    print(f"模型: {config.model}")
else:
    print("配置验证失败")

当天作业

AI 对话客户端

创建一个完整的 AI 对话客户端类。

参考答案:

import requests
import os
from dotenv import load_dotenv
from pydantic import BaseModel, Field
from typing import List, Dict, Optional
import json

load_dotenv()

class ChatMessage(BaseModel):
    """对话消息"""
    role: str = Field(..., pattern="^(system|user|assistant)$")
    content: str

class ChatConfig(BaseModel):
    """对话配置"""
    api_key: str = Field(default_factory=lambda: os.getenv("OPENAI_API_KEY", ""))
    model: str = "gpt-3.5-turbo"
    temperature: float = Field(0.7, ge=0, le=2)
    max_tokens: int = Field(2000, gt=0)

class AIChat:
    """AI 对话客户端"""
    
    def __init__(self, config: ChatConfig):
        self.config = config
        self.messages: List[ChatMessage] = []
        self.base_url = "https://api.openai.com/v1"
    
    def add_system_message(self, content: str):
        """添加系统消息"""
        msg = ChatMessage(role="system", content=content)
        self.messages.append(msg)
    
    def add_user_message(self, content: str):
        """添加用户消息"""
        msg = ChatMessage(role="user", content=content)
        self.messages.append(msg)
    
    def send(self) -> Optional[str]:
        """发送消息"""
        url = f"{self.base_url}/chat/completions"
        headers = {
            "Authorization": f"Bearer {self.config.api_key}",
            "Content-Type": "application/json"
        }
        data = {
            "model": self.config.model,
            "messages": [msg.model_dump() for msg in self.messages],
            "temperature": self.config.temperature,
            "max_tokens": self.config.max_tokens
        }
        
        try:
            response = requests.post(url, headers=headers, json=data, timeout=30)
            response.raise_for_status()
            result = response.json()
            content = result["choices"][0]["message"]["content"]
            
            # 保存助手回复
            assistant_msg = ChatMessage(role="assistant", content=content)
            self.messages.append(assistant_msg)
            
            return content
        except Exception as e:
            print(f"API 调用失败: {e}")
            return None
    
    def get_history(self) -> List[Dict]:
        """获取对话历史"""
        return [msg.model_dump() for msg in self.messages]
    
    def save_history(self, filename: str):
        """保存对话历史"""
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(self.get_history(), f, ensure_ascii=False, indent=2)

# 使用
config = ChatConfig(
    api_key="sk-xxx",
    model="gpt-3.5-turbo",
    temperature=0.7
)

chat = AIChat(config)
chat.add_system_message("你是一个Python助手")
chat.add_user_message("什么是类?")

# 发送并获取回复
response = chat.send()
if response:
    print(f"AI: {response}")

# 保存历史
chat.save_history("chat_history.json")

今日总结

核心知识点回顾

1. 面向对象

  • 类和对象:类是模板,对象是实例
  • __init__:初始化方法
  • self:指向对象本身
  • 实例方法、类方法、静态方法

2. 继承与多态

  • 继承:class Child(Parent)
  • 重写方法:子类定义同名方法
  • super():调用父类方法

3. 类型提示

  • 基础类型:int, str, float, bool
  • 复杂类型:List, Dict, Optional, Union
  • 函数注解:参数类型和返回值类型

4. dataclass

  • @dataclass 装饰器
  • 自动生成 __init____repr__
  • field():处理可变默认值

5. Pydantic

  • BaseModel:数据验证基类
  • Field:字段约束
  • validator:自定义验证器
  • model_dump():转为字典

6. HTTP 请求

  • requests.get/post
  • headers:请求头
  • json:JSON 数据
  • raise_for_status():检查状态码

7. 环境变量

  • os.getenv():读取环境变量
  • python-dotenv:加载 .env 文件
  • 避免硬编码敏感信息

Python vs Java 对比

特性PythonJava
类定义class Name:class Name {}
构造函数__init__(self, ...)Name(...)
this/selfself(显式)this(隐式)
继承class Child(Parent)class Child extends Parent
类型提示可选(typing)强制
数据类@dataclassrecord(Java 14+)

常见问题

Q:什么时候用函数,什么时候用类? A:数据和操作紧密相关时用类;简单工具函数用函数。

Q:self 是什么? A:指向当前对象的引用,类似 Java 的 this,但必须显式写出。

Q:类型提示是强制的吗? A:不是,Python 运行时不检查类型,但 IDE 和工具会用它提示错误。

Q:Pydantic 和 dataclass 的区别? A:dataclass 只是简化定义,Pydantic 还提供运行时验证。

Q:API Key 应该放哪里? A:.env 文件(不提交到 git)或环境变量,绝不硬编码。

重点注意事项

  1. __init__ 的 self:第一个参数必须是 self

  2. 可变默认值陷阱

    # 错误
    def __init__(self, items=[]):
        self.items = items  # 所有实例共享同一个列表
    
    # 正确
    def __init__(self, items=None):
        self.items = items if items else []
  3. 类型提示不影响运行:写错了不会报错,只是 IDE 提示

  4. .env 文件不要提交:添加到 .gitignore

  5. API 超时设置:requests 默认无超时,必须设置 timeout