Python 测试开发基础
Python 是什么
了解 Python 在测试开发中的角色和价值
Python 是一门简洁、易学、生态丰富的编程语言,在测试开发领域有着广泛的应用。它的核心优势包括:
一是语法接近自然语言,上手门槛低。
二是拥有丰富的第三方库,如 requests、pytest、playwright 等。
三是支持多种编程范式,可以面向过程、面向对象或函数式编程。
四是跨平台兼容性好,Windows、Mac、Linux 都能运行。
为什么测试开发要学 Python
学习 Python 的五大理由
- 【面试高频】Python 是测试开发岗位的主流语言,超过 80% 的测试开发岗位要求 Python 能力。面试官会用 Python 相关问题考察你的编程基础和工程化思维。
- 【快速落地】Python 语法简洁,能快速实现接口测试、数据清洗、脚本自动化等任务。一个复杂的接口测试脚本,Python 可能只需要 50 行,而 Java 可能需要 150 行。
- 【生态完善】测试领域的工具和框架大多优先支持 Python:pytest 是最流行的测试框架之一,requests 是最简单的 HTTP 客户端,Allure、locust、playwright 都有 Python 版本。
- 【团队协作】Python 代码可读性强,新人接手项目更容易理解。测试脚本往往是团队共享的资产,Python 的简洁性降低了协作成本。
- 【职业发展】掌握 Python 后可以扩展到测试平台开发、CI/CD 脚本、数据处理等领域,职业天花板更高。
Python vs Java/Go:测试开发怎么选
与其他语言的对比,帮助你选择适合的技术栈
- 【Python 优势】语法简洁,写测试脚本效率高。第三方库丰富,测试工具生态完善。学习曲线平缓,新手友好。
适合:接口测试、自动化脚本、数据驱动测试、快速原型验证。
- 【Java 优势】类型系统严格,大型项目更稳定。企业级框架成熟(TestNG、RestAssured)。性能更好,适合高并发场景。
适合:大型企业测试平台、需要与 Java 业务代码紧密集成的项目。
- 【Go 优势】编译速度快,部署简单(单文件二进制)。并发原生支持,性能接近 C。适合云原生场景。
适合:性能测试工具开发、测试平台后端服务。
- 【选择建议】如果刚开始学测试开发,首选 Python。如果团队技术栈是 Java,学 Java 更实际。如果要做测试平台后端,Go 是不错的选择。
前置知识:学 Python 前你需要会什么
明确学习 Python 的起点要求
- 【必须掌握】计算机基础操作(文件、目录概念)。文本编辑器使用(VS Code 或 PyCharm)。命令行基本操作(cd、ls、python 命令)。
- 【建议掌握】HTTP 协议基础(请求方法、状态码、Header)。JSON 数据格式。版本控制基础(Git 的 add、commit、push)。
- 【不需要掌握】算法与数据结构(测试脚本很少用到复杂算法)。操作系统原理。数据库底层实现。网络协议细节。
零基础第一步:10 分钟快速体验
完全没接触过 Python 的快速入门
如果你从未写过 Python 代码,按以下步骤快速体验:
第一步,安装 Python(从 python.org 下载,安装时勾选 “Add to PATH”)。
第二步,打开命令行,输入 python 进入交互模式。
第三步,输入 print(“Hello, 测试开发!”) 并回车,看到输出就成功了。
第四步,尝试简单计算:2 + 3,然后是变量赋值:name = “你的名字”。
这个 10 分钟体验让你对 Python 有直观感受,知道它能做什么。接下来就可以系统学习了。
分阶段学习建议
从零到能写项目级测试的四阶段学习路径
- 【第一阶段:基础语法】
目标:能写简单的脚本处理文件和数据。
内容:变量与数据类型、条件判断与循环、字符串操作、列表与字典、文件读写。
练习任务:写一个脚本读取 CSV 文件,统计某列数据的总和。
时间建议:每天 1-2 小时,约 1 周完成。
- 【第二阶段:函数与模块】
目标:能组织代码为可复用的函数和模块。
内容:函数定义与参数、模块导入与组织、异常处理(try-except)、常用内置模块(os、json、datetime)。
练习任务:封装一个读取 JSON 配置文件的函数。
时间建议:每天 1-2 小时,约 1 周完成。
- 【第三阶段:测试框架入门】
目标:能用 pytest 写基本的测试用例。
内容:pytest 基本用法、断言与报告、Fixture 初步、参数化测试。
练习任务:为一个计算器函数写 5 个测试用例。
时间建议:每天 1-2 小时,约 1.5 周完成。
- 【第四阶段:工程化能力】
目标:能搭建一个小型测试项目。
内容:项目结构设计、配置管理、日志封装、请求客户端封装。
练习任务:搭建一个接口测试项目骨架,包含配置、日志、请求封装。
时间建议:每天 1-2 小时,约 2 周完成。
时间投入建议
不同时间投入的学习周期建议
不同投入的学习周期:
【每天 1-2 小时】完整掌握四个阶段约需 5-6 周,适合在职学习。
【每天 30 分钟】完整掌握约需 10-12 周,适合碎片时间学习。
【集中学习(全天)】约 2 周可完成基础到框架入门。建议初学者选择每天 1-2 小时的节奏,太慢容易遗忘,太快容易消化不良。
学习资源推荐
推荐的学习资源和实践建议
- 【官方文档】Python 官方教程是最权威的参考,遇到不确定的用法先查文档。
- 【实践建议】边学边写,不要只看不练。每个概念至少写 3 个小例子验证理解。
- 【项目驱动】学完基础后,用真实需求驱动学习,比如给日常工作写自动化脚本。
实操案例:从流程理解真实应用
实操案例说明
以下三个案例按难度递进,帮助你理解 Python 在测试开发中的实际应用。每个案例都用流程描述而非完整代码,重点理解思路和设计要点。
案例 0:写你的第一个 Python 测试(入门)
10 分钟完成的入门案例
- 【目标】用 Python 写一个简单的测试函数,验证一个计算器模块。
- 【步骤流程】
第一步:创建测试文件 test_calculator.py。
第二步:导入被测试的函数(如 add、subtract)。
第三步:定义测试函数,命名以 test_ 开头。
第四步:在函数内写断言,如 assert add(1, 2) == 3。
第五步:运行 pytest 命令查看结果。
- 【学到什么】测试文件命名规范、测试函数命名规范、断言的基本写法、pytest 的运行方式。
- 【下一步】尝试测试一个真实的接口,用 requests 发送请求并断言响应。
示例代码:计算器测试
# calculator.py - 被测试的模块def add(a: int, b: int) -> int: """加法运算""" return a + b
def subtract(a: int, b: int) -> int: """减法运算""" return a - b
def multiply(a: int, b: int) -> int: """乘法运算""" return a * b
def divide(a: int, b: int) -> float: """除法运算""" if b == 0: raise ValueError("除数不能为零") return a / b# test_calculator.py - 测试文件import pytestfrom calculator import add, subtract, multiply, divide
def test_add(): """测试加法""" assert add(1, 2) == 3 assert add(-1, 1) == 0 assert add(0, 0) == 0
def test_subtract(): """测试减法""" assert subtract(5, 3) == 2 assert subtract(0, 5) == -5
def test_multiply(): """测试乘法""" assert multiply(3, 4) == 12 assert multiply(0, 100) == 0
def test_divide(): """测试除法""" assert divide(10, 2) == 5.0 assert divide(7, 2) == 3.5
def test_divide_by_zero(): """测试除零异常""" with pytest.raises(ValueError, match="除数不能为零"): divide(1, 0)示例代码:虚拟环境管理
# 创建虚拟环境python -m venv .venv
# 激活虚拟环境(Windows).venv\Scripts\activate
# 激活虚拟环境(Mac/Linux)source .venv/bin/activate
# 安装依赖pip install pytest requests
# 导出依赖pip freeze > requirements.txt
# 安装依赖(从文件)pip install -r requirements.txt案例 1:封装一个请求客户端(基础)
理解面向对象封装的核心设计
- 【目标】封装一个 HTTP 客户端,统一处理请求、响应、日志和异常。
- 【设计思路】创建一个 HttpClient 类,初始化时设置基础 URL 和默认超时。封装 get、post 等方法,内部处理请求日志、超时重试、异常捕获。返回时统一格式,包含状态码、响应体、耗时等信息。
- 【关键流程】初始化 -> 设置 session 和默认配置 -> 发送请求 -> 记录日志 -> 处理异常 -> 统一返回格式。
- 【关键要点】请求前记录 URL、参数、请求体。请求后记录响应状态、耗时。网络异常自动重试。业务异常返回错误信息但不抛出。所有接口调用走这个客户端,便于统一维护。
示例代码:HTTP 请求客户端封装
import loggingimport timefrom typing import Any, Optionalimport requestsfrom requests.adapters import HTTPAdapterfrom urllib3.util.retry import Retry
logger = logging.getLogger(__name__)
class HttpClient: """HTTP 请求客户端封装"""
def __init__(self, base_url: str, timeout: int = 30, max_retries: int = 3): self.base_url = base_url.rstrip("/") self.timeout = timeout self.session = requests.Session()
# 配置重试策略 retry_strategy = Retry( total=max_retries, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) self.session.mount("http://", adapter) self.session.mount("https://", adapter)
def _build_url(self, path: str) -> str: """拼接完整 URL""" return f"{self.base_url}/{path.lstrip('/')}"
def request( self, method: str, path: str, params: Optional[dict] = None, json: Optional[dict] = None, headers: Optional[dict] = None, ) -> dict[str, Any]: """发送请求并返回统一格式的结果""" url = self._build_url(path) start_time = time.time()
logger.info(f"请求开始: {method} {url}, params={params}")
try: response = self.session.request( method=method, url=url, params=params, json=json, headers=headers, timeout=self.timeout, ) elapsed = time.time() - start_time
logger.info( f"请求完成: {response.status_code}, 耗时={elapsed:.3f}s" )
return { "status_code": response.status_code, "body": response.json(), "headers": dict(response.headers), "elapsed": elapsed, } except requests.exceptions.Timeout: logger.error(f"请求超时: {method} {url}") raise except requests.exceptions.RequestException as e: logger.error(f"请求异常: {method} {url}, error={e}") raise
def get(self, path: str, params: Optional[dict] = None, **kwargs) -> dict: """GET 请求""" return self.request("GET", path, params=params, **kwargs)
def post(self, path: str, json: Optional[dict] = None, **kwargs) -> dict: """POST 请求""" return self.request("POST", path, json=json, **kwargs)
def set_token(self, token: str): """设置认证 Token""" self.session.headers.update({"Authorization": f"Bearer {token}"})
# 使用示例if __name__ == "__main__": client = HttpClient("https://api.example.com") resp = client.get("/users", params={"page": 1}) print(resp["status_code"], resp["body"])案例 2:数据驱动测试(进阶)
理解数据驱动测试的设计模式
- 【目标】将测试数据与测试逻辑分离,一份代码跑多组数据。
- 【设计思路】测试数据存储在独立的 YAML 或 JSON 文件。测试函数通过参数化机制读取数据。每组数据作为独立测试用例执行,失败不影响其他组。
- 【流程描述】
第一步:准备数据文件,每条数据包含输入参数和预期结果。
第二步:写数据加载函数,从文件读取并解析为列表。
第三步:使用 pytest.mark.parametrize 装饰器,将数据注入测试函数。
第四步:在测试函数内,用参数执行请求并断言结果。
- 【关键要点】数据文件格式清晰,字段名有意义。数据覆盖正常流程、边界条件、异常场景。失败时能看出是哪组数据失败。数据与代码分开维护,改数据不改代码。
示例代码:数据驱动测试
# 登录测试数据- name: 正常登录 username: "admin" password: "admin123" expected: { status_code: 200, success: true }
- name: 密码错误 username: "admin" password: "wrong_password" expected: { status_code: 401, success: false }
- name: 用户名为空 username: "" password: "admin123" expected: { status_code: 400, success: false }
- name: 密码为空 username: "admin" password: "" expected: { status_code: 400, success: false }# test_login.py - 数据驱动测试import pytestimport yamlfrom pathlib import Path
def load_test_data(file_path: str) -> list: """从 YAML 文件加载测试数据""" path = Path(__file__).parent / file_path with open(path, "r", encoding="utf-8") as f: return yaml.safe_load(f)
# 加载数据文件login_cases = load_test_data("test_data/login_cases.yaml")
@pytest.mark.parametrize("case", login_cases, ids=[c["name"] for c in login_cases])def test_login(case, api_client): """数据驱动的登录测试""" resp = api_client.post("/login", json={ "username": case["username"], "password": case["password"], })
assert resp["status_code"] == case["expected"]["status_code"] assert resp["body"]["success"] == case["expected"]["success"]示例代码:装饰器实战
import functoolsimport timefrom typing import Callable, Any
def timer(func: Callable) -> Callable: """性能计时装饰器""" @functools.wraps(func) def wrapper(*args, **kwargs) -> Any: start = time.time() result = func(*args, **kwargs) elapsed = time.time() - start print(f"{func.__name__} 执行耗时: {elapsed:.3f}s") return result return wrapper
def retry(max_attempts: int = 3, delay: float = 1.0): """重试装饰器""" def decorator(func: Callable) -> Callable: @functools.wraps(func) def wrapper(*args, **kwargs) -> Any: for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: if attempt == max_attempts - 1: raise print(f"第 {attempt + 1} 次失败: {e}, {delay}s 后重试...") time.sleep(delay) return wrapper return decorator
# 使用示例@timer@retry(max_attempts=3, delay=1.0)def fetch_data(url: str) -> dict: """获取数据,带重试和计时""" import requests return requests.get(url, timeout=10).json()常见误区:初学者最容易踩的坑
误区说明
以下是 Python 测试开发中常见的五个误区,了解这些误区可以帮助你避免踩坑,也能在面试中展示你的经验。
误区 1:只学语法,不练项目
错误表现
花大量时间看语法教程、背语法规则,但从未写过完整的项目。面试时问语法都对,问项目就卡壳。
为什么错
测试开发需要的不是语法知识,而是用 Python 解决问题的能力。语法只是工具,项目经验才是能力证明。
正确做法
学完基础语法后,立即开始写小项目:先写一个读取配置文件的脚本,再写一个接口测试小项目,逐步积累。每学一个概念,就在项目中用起来。
面试应对
面试官问 Python 能力时,不要罗列语法,而是讲项目:“我用 Python 封装了请求客户端,实现了自动重试和日志记录。用 pytest 写了 200+ 个测试用例,实现了数据驱动测试框架。“
误区 2:忽略异常处理和日志
错误表现
代码里没有 try-except,出错就崩溃。没有日志记录,出问题不知道哪里挂了。
为什么错
测试脚本是长期运行的资产,没有异常处理会导致一个小错误中断整个测试流程。没有日志,定位问题只能靠猜。
正确做法
异常处理分层:网络异常自动重试,业务异常记录详情,框架异常上报监控。日志分级别:DEBUG 记录详细信息,INFO 记录关键流程,ERROR 记录失败原因。每个关键操作都要有日志。
面试应对
主动说明你的异常处理策略:“我的框架有三层异常处理。网络层异常自动重试 3 次,业务层异常记录请求响应详情,框架层异常上报到监控系统。日志按级别配置,关键操作都有记录。“
误区 3:不会用虚拟环境
错误表现
所有项目共用一个 Python 环境,安装包时不管版本冲突。换电脑时不知道怎么复现环境。
为什么错
不同项目依赖不同版本的包,共用环境会导致冲突。没有环境隔离,代码在其他机器上可能跑不起来。
正确做法
每个项目使用独立的虚拟环境(venv 或 conda)。用 requirements.txt 或 pyproject.toml 记录依赖。换机器时一行命令就能复现环境。提交代码时把依赖文件一起提交。
面试应对
说明你的环境管理实践:“我用 venv 为每个项目创建独立环境,依赖记录在 requirements.txt。新人 clone 代码后,创建虚拟环境、安装依赖、就能运行测试,不需要额外配置。“
误区 4:代码不封装,复制粘贴堆代码
错误表现
每个测试用例都复制粘贴相似的代码,改一处要改十处。没有函数、没有类,全是线性脚本。
为什么错
复制粘贴的代码难以维护,出问题时改一个地方可能漏改其他地方。代码量越大,维护成本越高。
正确做法
识别重复代码,封装为函数。相关的函数组织为类。遵循 DRY 原则。
例如:所有接口调用封装到 HttpClient 类,所有断言封装到 AssertHelper 类,所有数据构建封装到 DataBuilder 类。
面试应对
展示你的封装意识:“我的框架有三层封装。基础层封装请求客户端、日志、配置读取。业务层封装登录、下单、支付等业务操作。用例层只关注测试逻辑,其他都复用基础层和业务层的封装。“
误区 5:面试时只会说语法,不会讲工程化
错误表现
面试官问 Python 能力,回答是:“我会变量、循环、函数、类。“只停留在语法层面。
为什么错
测试开发岗位看重的是工程化能力:你能用 Python 搭建可维护的测试框架吗?你能处理异常、日志、配置吗?语法只是基础,工程化才是核心竞争力。
正确做法
准备工程化话题:配置管理(如何管理多环境配置)、日志系统(如何设计日志级别和格式)、异常处理(如何分层处理不同类型的异常)、项目结构(如何组织代码让新人容易上手)。
面试应对
用工程化语言回答:“我的 Python 能力体现在三个层面。基础层面:熟练使用语法特性,理解装饰器、生成器、上下文管理器。框架层面:用 pytest 搭建测试框架,实现了 Fixture 管理、参数化、报告生成。工程层面:设计了配置管理、日志系统、异常处理机制,让框架可维护、可扩展。“
误区总结
五大误区对照总结
- 误区 1:只学语法不练项目 -> 边学边做,用项目驱动学习
- 误区 2:忽略异常处理和日志 -> 异常分层处理,日志分级记录
- 误区 3:不会用虚拟环境 -> 每个项目独立环境,依赖文件化管理
- 误区 4:代码不封装堆复制 -> 识别重复代码,函数和类封装
- 误区 5:面试只讲语法 -> 准备工程化话题,展示框架设计能力
面试问答:如何把知识讲清楚
面试问答说明
以下是 Python 测试开发面试中高频出现的 5 个问题,每个问题包含回答骨架、深度答案和追问应对。P0 表示必准备,P1 表示加分项。
Q1:你在自动化框架里封装过哪些公共能力?(P0)
回答骨架
列出 3-4 个封装模块 -> 说明每个模块解决的问题 -> 强调封装带来的收益(可维护性、可复用性)。
深度答案
我封装过以下公共能力:
【请求客户端】统一处理超时、代理、日志记录和鉴权注入,所有接口调用都走这个客户端,便于统一维护和升级。
【鉴权模块】自动刷新 token 并注入请求头,测试用例不需要关心登录状态。
【重试机制】支持指数退避和自定义重试条件,网络抖动时自动重试。
【通用断言层】包含状态码校验、字段结构校验和业务断言,让断言代码更简洁。
【数据构建器】链式构建测试数据,避免复杂的字典嵌套代码。
追问应对
追问:这些封装怎么设计的?
回答:我采用分层设计。基础层是工具类(请求、日志、配置),不依赖业务。业务层封装业务操作(登录、下单),依赖基础层。用例层只关注测试逻辑,复用前两层。
追问:遇到过什么问题?
回答:早期把所有封装都放在一个文件里,后来拆分为独立模块,用依赖注入解耦,代码更清晰了。
Q2:装饰器、生成器、上下文管理器在项目里怎么用过?(P0)
回答骨架
每个特性举一个实际应用场景 -> 说明为什么用它 -> 展示工程化思维。
深度答案
【装饰器】用于自动重试、日志记录和性能计时。\n\n比如 @retry 装饰器在接口超时时自动重试,@timer 装饰器记录函数执行时间。装饰器让这些横切关注点与业务逻辑解耦。
【生成器】用于处理大量测试数据时的惰性加载。\n\n比如测试数据有 10 万条,用生成器逐条读取,避免一次性加载导致内存溢出。
【上下文管理器】用于资源管理,比如数据库连接、文件操作的自动打开和关闭。用 with 语句确保资源正确释放,即使中间发生异常也能清理。
追问应对
追问:装饰器的原理是什么?
回答:装饰器本质是一个高阶函数,接收一个函数,返回一个增强后的函数。可以用 @语法糖,也可以手动调用。
追问:生成器和列表的区别?
回答:生成器是惰性求值,只在需要时才计算下一个值,内存占用小。列表是一次性加载所有值。大数据场景首选生成器。
Q3:你如何组织配置、日志、断言和数据驱动?(P0)
回答骨架
每个模块说明设计思路 -> 强调解耦和可维护性 -> 举一个实际例子。
深度答案
【配置管理】按环境拆分配置文件(dev.yaml、test.yaml、prod.yaml)。使用 dataclass 定义配置结构,通过环境变量切换环境。配置集中管理,改配置不需要改代码。
【日志系统】统一配置日志格式(时间、级别、模块、信息)和输出位置(控制台、文件)。关键操作记录请求响应和耗时。失败时日志包含完整的上下文信息。
【断言设计】分层设计:协议层断言检查状态码、响应结构。业务层断言检查字段值、状态流转。链路层断言检查数据库、缓存。每层断言有清晰的错误信息。
【数据驱动】测试数据存储在独立的 YAML 或 JSON 文件。
通过参数化机制注入用例。数据和逻辑分离,改数据不需要改代码。
追问应对
追问:敏感配置怎么处理?
回答:敏感信息(密码、密钥)不写在配置文件里,通过环境变量注入。CI/CD 系统的安全变量存储敏感配置,运行时注入。
追问:数据驱动的数据格式怎么设计?
回答:每条数据包含用例描述、输入参数、预期结果、标签(用于筛选)。格式扁平化,便于维护。
Q4:面向对象在测试框架里怎么应用?(P1)
回答骨架
说明为什么要用面向对象 -> 举 2-3 个类的例子 -> 强调封装、继承、复用的收益。
深度答案
面向对象让代码更好组织和复用。
【封装】把请求客户端封装为 Session 类,内部处理认证、重试、日志。测试用例只需要实例化并调用方法,不需要关心内部实现。
【继承】把通用操作封装为 BasePage 类(页面对象模式),具体页面继承 BasePage,复用通用方法(等待元素、截图等)。
【组合】测试数据构建器支持链式调用,每个方法返回 self,可以组合构建复杂对象。\n\n例如:DataBuilder().with_name(“test”).with_age(18).build()。
追问应对
追问:什么时候用继承,什么时候用组合?
回答:“is-a” 关系用继承(Dog is an Animal),“has-a” 关系用组合(Car has an Engine)。测试框架里更多用组合和封装,继承用得少。
追问:过度封装怎么避免?
回答:封装要适度。一个类只做一件事(单一职责),一个方法不超过 30 行。先写用例,发现重复再封装,不要提前封装。
Q5:Python 有哪些坑需要避免?(P1)
回答骨架
举 3 个具体坑 -> 每个说明表现、原因、解决方法 -> 展示实战经验。
深度答案
【坑 1:可变默认参数】函数参数默认值是列表或字典时,多次调用会共享同一个对象。解决方法:默认值用 None,函数内部创建新对象。
【坑 2:闭包变量问题】循环中创建闭包,所有闭包共享同一个变量。解决方法:使用默认参数捕获当前值,或用 lambda 带默认参数。
【坑 3:浅拷贝问题】列表嵌套列表时,浅拷贝只复制外层,内层还是引用。解决方法:用 copy.deepcopy 进行深拷贝。
【坑 4:编码问题】Python 2 和 Python 3 字符串处理不同。解决方法:统一使用 Python 3,字符串都用 str 类型,注意文件读写的 encoding 参数。
追问应对
追问:遇到过其他坑吗?
回答:还有全局变量污染问题。测试用例之间不应该共享可变全局变量,会导致用例相互影响。
解决方法是用 Fixture 注入依赖,或使用上下文管理器。
追问:怎么学习这些坑?
回答:多写代码,踩坑后记录下来。Code Review 时互相检查。看开源项目的 issue 和 bug 修复记录。
面试回答的核心技巧
面试回答的核心技巧总结
- 【结构化回答】先说骨架,再展开细节。面试官记住的是结构,不是细节。
- 【用例子说话】不要只说概念,举一个实际项目的例子。“我封装了请求客户端”不如”我封装的请求客户端,统一处理了重试、日志和鉴权,让 200+ 测试用例的代码量减少了 30%。”
- 【强调收益】每个能力都要说明解决了什么问题、带来了什么收益。面试官想知道的是你的贡献。
- 【准备好追问】回答完要有意识地引导追问方向,把话题引向你准备充分的地方。
- 【展示工程思维】测试开发不只是写脚本,是搭建可维护、可扩展的测试体系。面试时多从工程角度思考问题。