当前位置: 首页 > news >正文

Python 类实战:从“函数堆函数”到“客户端对象”,看类如何让 API 请求代码脱胎换骨

目录

一、从代码工作的真实痛点说起:API 客户端开发

不用类的写法:函数+全局变量,越写越“脆”

用类的写法:封装客户端与业务,代码像搭积木

第一步:定义基础 API 客户端类

第二步:扩展用户模块客户端 → 复用基类,只写业务逻辑

第三步:扩展订单模块客户端 → 同样复用基类

对比总结:类的第一个优势——封装性

二、类的第二个优势:继承复用,拒绝重复造轮子

不用类的写法:复制粘贴,改两个参数

用类的写法:继承基类,仅覆盖配置

使用测试客户端:

对比总结:类的第二个优势——继承复用

三、类的第三个优势:多态,统一调用不同模块的接口

不用类的写法:判断类型,代码臃肿

用类的写法:多态,统一接口

监控脚本简化为:

对比总结:类的第三个优势——多态性

四、类的终极优势:让代码更“工程化”

五、什么时候该用类?

六、总结:类是代码工程的“积木套装”


一、从代码工作的真实痛点说起:API 客户端开发

在后端开发或自动化测试中,调用第三方/内部 API是高频需求。假设我们要做一个 电商系统的 API 客户端,需要支持:

  • 发送 GET/POST 请求;

  • 自动携带认证 Token;

  • 处理通用错误(如 401 重刷 Token、500 重试);

  • 支持不同业务线(用户模块、订单模块)的个性化参数。

不用类的写法:函数+全局变量,越写越“脆”

新手常犯的错误是用全局变量存配置,用独立函数处理每个接口:

import requests# 全局变量存配置 → 容易被意外修改,无法区分环境
BASE_URL = "https://api.example.com"
ACCESS_TOKEN = "旧Token"# 通用请求函数 → 每个接口都要调用它,重复写参数拼接
def send_request(method: str, endpoint: str, params=None, data=None):url = f"{BASE_URL}{endpoint}"headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}try:response = requests.request(method, url, params=params, json=data, headers=headers)if response.status_code == 401:# 401 时重刷 Token → 但全局变量会被所有请求共享,可能引发竞态条件new_token = refresh_token()  # 假设这是刷新 Token 的函数global ACCESS_TOKENACCESS_TOKEN = new_tokenreturn send_request(method, endpoint, params, data)  # 递归重试response.raise_for_status()  # 其他错误直接抛出return response.json()except Exception as e:print(f"请求失败:{e}")return None# 用户模块接口 → 每个接口都要单独写参数逻辑
def get_user(user_id: int):return send_request("GET", f"/users/{user_id}")def update_user(user_id: int, name: str):return send_request("POST", f"/users/{user_id}", data={"name": name})# 订单模块接口 → 重复造轮子
def get_order(order_id: int):return send_request("GET", f"/orders/{order_id}")  # 参数拼接逻辑和 get_user 几乎一样

这段代码的问题有多致命?

  1. 配置脆弱BASE_URLACCESS_TOKEN是全局变量,测试时改了可能影响其他模块,线上环境切换还要手动改值;

  2. 重复劳动:每个接口都要写 send_request+ 端点拼接,get_userget_order逻辑高度重复;

  3. 错误处理耦合send_request里的 401 重刷 Token 逻辑,一旦写错会影响所有接口,且全局 Token 可能被多个线程同时修改;

  4. 扩展困难:新增“商品模块”时,又要复制 get_user的代码,无法复用已有逻辑。

用类的写法:封装客户端与业务,代码像搭积木

类的封装特性可以把“配置”“公共逻辑”“业务接口”绑在一起,形成独立的“API 客户端对象”。

第一步:定义基础 API 客户端类
import requestsclass BaseAPIClient:def __init__(self, base_url: str, access_token: str):self.base_url = base_url  # 实例属性:每个客户端有自己的 base_urlself.access_token = access_token  # 实例属性:避免全局变量污染self.session = requests.Session()  # 用 Session 保持连接,提升性能self.session.headers.update({"Authorization": f"Bearer {access_token}"})# 封装通用请求逻辑 → 所有子类复用def _send_request(self, method: str, endpoint: str, params=None, data=None):url = f"{self.base_url}{endpoint}"try:response = self.session.request(method, url, params=params, json=data)if response.status_code == 401:# 401 时重刷 Token → 只修改当前实例的 token,不影响其他客户端new_token = self._refresh_token()  # 子类可重写此方法实现自定义刷新逻辑self.access_token = new_tokenself.session.headers.update({"Authorization": f"Bearer {new_token}"})return self._send_request(method, endpoint, params, data)  # 递归重试response.raise_for_status()return response.json()except Exception as e:print(f"[{self.__class__.__name__}] 请求失败:{e}")return None# 抽象方法:子类必须实现 Token 刷新逻辑(强制约束)def _refresh_token(self):raise NotImplementedError("子类需实现 _refresh_token 方法")
第二步:扩展用户模块客户端 → 复用基类,只写业务逻辑
class UserAPIClient(BaseAPIClient):def __init__(self, base_url: str, access_token: str):super().__init__(base_url, access_token)  # 调用父类初始化,复用配置和 Session# 实现 Token 刷新逻辑 → 用户模块可能有专属刷新方式def _refresh_token(self):# 假设用户模块刷新 Token 调这个接口refresh_url = f"{self.base_url}/auth/refresh"response = self.session.post(refresh_url)new_token = response.json()["token"]return new_token# 用户相关接口 → 直接调用基类的 _send_request,无需重复写 URL 拼接def get_user(self, user_id: int):return self._send_request("GET", f"/users/{user_id}")def update_user(self, user_id: int, name: str):return self._send_request("POST", f"/users/{user_id}", data={"name": name})
第三步:扩展订单模块客户端 → 同样复用基类
class OrderAPIClient(BaseAPIClient):def __init__(self, base_url: str, access_token: str):super().__init__(base_url, access_token)# 订单模块可能用不同的 Token 刷新方式def _refresh_token(self):# 假设订单模块刷新 Token 调另一个接口refresh_url = f"{self.base_url}/order/auth/refresh"response = self.session.post(refresh_url)return response.json()["token"]def get_order(self, order_id: int):return self._send_request("GET", f"/orders/{order_id}")

对比总结:类的第一个优势——封装性

不用类时,配置和逻辑散落在全局变量和函数中,像“散落的零件”;

用类时,配置(base_url/access_token)、公共逻辑(_send_request)、业务接口(get_user/get_order)被封装在对象里,代码更“紧凑”且安全。

二、类的第二个优势:继承复用,拒绝重复造轮子

假设需求升级:需要支持测试环境生产环境的 API 客户端,两者只有 base_urlaccess_token不同,其他逻辑(如 Token 刷新、请求发送)完全一致。

不用类的写法:复制粘贴,改两个参数

# 测试环境客户端 → 几乎复制了生产环境的代码,只改了 BASE_URL 和 ACCESS_TOKEN
TEST_BASE_URL = "https://test-api.example.com"
TEST_ACCESS_TOKEN = "测试Token"def test_send_request(method, endpoint, params=None, data=None):url = f"{TEST_BASE_URL}{endpoint}"headers = {"Authorization": f"Bearer {TEST_ACCESS_TOKEN}"}# ... 重复写请求逻辑(和之前的 send_request 几乎一样)

用类的写法:继承基类,仅覆盖配置

# 测试环境客户端 → 继承 BaseAPIClient,只改初始化参数
class TestUserAPIClient(UserAPIClient):def __init__(self):# 直接传入测试环境的 base_url 和 tokensuper().__init__(base_url="https://test-api.example.com",access_token="测试Token")
使用测试客户端:
test_client = TestUserAPIClient()
test_client.get_user(123)  # 自动使用测试环境的配置和逻辑

对比总结:类的第二个优势——继承复用

不用类时,环境切换要复制粘贴整个函数;

用类时,通过继承复用父类所有逻辑,仅修改初始化参数,代码量减少90%,且避免遗漏关键配置。

三、类的第三个优势:多态,统一调用不同模块的接口

假设我们需要一个监控脚本,定期调用用户和订单接口检查数据。

不用类的写法:判断类型,代码臃肿

def monitor_data(clients):for client in clients:if isinstance(client, UserAPIClient):  # 判断是不是用户客户端user_info = client.get_user(123)print(f"用户信息:{user_info}")elif isinstance(client, OrderAPIClient):  # 判断是不是订单客户端order_info = client.get_order(456)print(f"订单信息:{order_info}")

用类的写法:多态,统一接口

注意到 UserAPIClientOrderAPIClient都继承自 BaseAPIClient,且都有 get_user/get_order等业务方法。但如果我们定义一个统一的数据获取接口(比如 fetch_data),可以通过多态简化调用:

# 在基类中定义抽象方法 → 强制子类实现
class BaseAPIClient:# ... 其他代码不变 ...def fetch_data(self):raise NotImplementedError("子类需实现 fetch_data 方法")# 用户模块客户端 → 实现 fetch_data
class UserAPIClient(BaseAPIClient):def fetch_data(self):return self.get_user(123)  # 具体业务逻辑# 订单模块客户端 → 实现 fetch_data
class OrderAPIClient(BaseAPIClient):def fetch_data(self):return self.get_order(456)  # 具体业务逻辑
监控脚本简化为:
def monitor_data(clients):for client in clients:data = client.fetch_data()  # 统一调用 fetch_data 方法print(f"{client.__class__.__name__} 数据:{data}")

对比总结:类的第三个优势——多态性

不用类时,监控不同模块要写类型判断;

用类时,通过统一接口(fetch_data)调用不同实现,代码简洁且易新增模块(比如新增商品模块,只需实现 fetch_data)。

四、类的终极优势:让代码更“工程化”

类的本质是对“客户端”这一实体的建模

  • 客户端有状态base_url/access_token);

  • 客户端有行为(发送请求、刷新 Token);

  • 不同客户端(用户/订单/测试)是“客户端”的不同“变种”。

用类的方式写 API 客户端,就像在组装一台机器:

“我需要一个用户 API 客户端,它继承自基础客户端,配置测试环境的 URL 和 Token,能获取用户数据。”

而不用的类的方式,更像在拼乐高:

“我要找测试环境的 URL,找测试 Token,写请求逻辑,处理错误……”

五、什么时候该用类?

类在以下场景中能显著提升代码质量:

  1. 需要管理状态:比如 API 客户端的 base_url/access_token

  2. 需要复用公共逻辑:比如请求发送、错误处理;

  3. 需要环境隔离:比如测试/生产环境的不同配置;

  4. 需要统一接口:比如监控脚本调用不同模块的接口。

六、总结:类是代码工程的“积木套装”

类的核心不是语法,而是组织代码的思维方式

  • 封装把数据和行为绑在一起,避免混乱;

  • 继承复用公共逻辑,拒绝重复;

  • 多态统一接口,简化调用。

回到最初的 API 客户端需求,用类后的代码:

  • 更安全:配置封装在对象里,避免全局污染;

  • 更易维护:修改请求逻辑只需改基类;

  • 更易扩展:新增模块只需继承基类,实现业务方法。

如果你还在用“函数堆函数”写 API 客户端,不妨试试用类重构——你会发现,代码从“东拼西凑的补丁”变成了“可组装的积木”!

最后留个小练习

试试用类实现一个“数据库连接客户端”,要求:

  1. 支持 MySQL 和 PostgreSQL,通过继承实现;

  2. 封装连接、查询、关闭连接的公共逻辑;

  3. 处理连接超时错误,自动重连;

  4. 提供统一的 execute_query方法执行 SQL。

欢迎在评论区分享你的代码~

关注我,后续会写更多 Python 面向对象的高级技巧(比如魔法方法、依赖注入、设计模式)!

http://www.dtcms.com/a/577466.html

相关文章:

  • springboot的单元测试功能有什么用
  • 5昌平区网站建设免费模板网站哪个好
  • 济南网站制作设计公司网站建设属于什么科目
  • 深入解析Kafka的消息模型:如何确保消息不丢失且高效传递
  • 微服务之Nacos(注册中心、配置中心)
  • 导致Resources文件夹的资源在Android打包后丢失的原因
  • Leetcode 46
  • Zabbix 7 概述与配置详解
  • 网站优化体验报告中国创业网
  • 用 FastAPI + Pydantic 打造“可验证、可热载、可覆盖”的配置中心
  • 2025教资面试真题电子版|科目试讲+结构化真题解析|完整PDF
  • 一文了解-大语言模型训练 vs 推理:硬件算力需求数据对比
  • 影刀RPA一键分析用户行为!AI智能画像,转化率提升300%[特殊字符]
  • Spring Cache快速入门
  • 网站底部横条导航代码做网站的怎么挣钱、
  • 【科研绘图系列】R语言绘制散点图(scatter plot)
  • Supabase 概述
  • 【微服务】(3) 服务注册与发现
  • 网站综合查询工具做推文的编辑网站
  • Prometheus实战教程 05 - 告警通知实现 - 邮件 + 钉钉 + 自定义告警模板
  • SELinux 故障排除完全指南:从拒绝访问到快速修复
  • 【Linux】Socket编程预备及UDP
  • 建站运营新闻网页设计需要学什么学历
  • 开题报告之基于SpringBoot框架的图书借阅系统的设计与实现
  • 金融RAG落地之痛:不在模型,而在数据结构
  • Spring Boot 中数据源自动配置的核心流程
  • Java HashMap深度解析:数据结构、原理与实战指南
  • 宁夏建设网站的公司电话大学生为什么不去中建
  • android su执行命令
  • 面向强化学习的状态空间建模:RSSM的介绍和PyTorch实现(2)