Python--测试代码

目录

1、使用pip安装pytest

1.1 更新pip

1.2 安装putest

2、测试函数

2.1 单元测试和测试用例

2.2 可通过的测试

2.3 运行测试

2.4 未通过的测试

2.5 解决测试未通过

2.6 添加新测试

3、测试类

3.1 各种断言

3.2 一个测试的类

3.3 测试AnonymousSurvey类

3.4 使用夹具


在编写函数或类时,还可为其编写测试。通过测试,可确定代码⾯对 各种输⼊都能够按要求⼯作。

1、使用pip安装pytest

第三⽅包(third-party package)指的是独⽴于 Python 核 ⼼的库。

然⽽,很多包并未被纳⼊标准库,因此得以独⽴于 Python 语⾔本⾝的更新 计划。为很多重要的功能是使⽤第三⽅包实 现的。

1.1 更新pip

Python 提供了⼀款名为 pip 的⼯具,可⽤来安装第三⽅包。因为 pip 帮我们

安装来⾃外部的包,所以更新频繁,以消除潜在的安全问题。有鉴于此,

我们先来更新 pip。

打开⼀个终端窗⼝,执⾏如下命令:

$ python -m pip install --upgrade pip 
❶ Requirement already satisfied: pip in /.../python3.11/site-packages 
(22.0.4) 
--snip-- 
❷ Successfully installed pip-22.1.2 

可使⽤下⾯的命令更新系统中安装的任何包:

$ python -m pip install --upgrade package_name

1.2 安装putest

将 pip 升级到最新版本后,就可以安装 pytest 了:

$ python -m pip install --user pytest 
Collecting pytest 
--snip-- 
Successfully installed attrs-21.4.0 iniconfig-1.1.1 ...pytest-7.x.x 

这⾥使⽤的核⼼命令也是 pip install,但指定的标志不是 --upgrade,⽽是 --user。这个标志让 Python 只为当前⽤户装指定的 包。

可使⽤下⾯的命令安装众多的第三⽅包:

$ python -m pip install --user package_name 

注意:如果在执⾏这个命令时遇到⿇烦,可尝试在不指定标志 --user 的情况下再次执⾏它。

2、测试函数

要学习测试,必须有要测试的代码。下⾯是⼀个简单的函数,它接受名和 姓并返回格式规范的姓名:

def get_formatted_name(first, last): """⽣成格式规范的姓名""" full_name = f"{first} {last}" return full_name.title()

我们编写⼀个使⽤这个 函数的程序。程序 names.py 让⽤户输⼊名和姓,并显⽰格式规范的姓名:

from name_function import get_formatted_name 
print("Enter 'q' at any time to quit.") 
while True: first = input("\nPlease give me a first name: ") if first == 'q': break last = input("Please give me a last name: ") if last == 'q': break formatted_name = get_formatted_name(first, last) print(f"\tNeatly formatted name: {formatted_name}.") 

这个程序从 name_function.py 中导get_formatted_name()。⽤户可 输⼊⼀系列名和姓,并看到格式规范的姓名:

Enter 'q' at any time to quit. 
Please give me a first name: janis 
Please give me a last name: joplin Neatly formatted name: Janis Joplin.Please give me a first name: bob 
Please give me a last name: dylan Neatly formatted name: Bob Dylan. 
Please give me a first name: q 

从上述输出可知,合并得到的姓名正确⽆误。

现在假设要修改 get_formatted_name(),使其还能够处理中间名。为此,可在每 次修get_formatted_name() 后都进⾏测试:运⾏程序 names.py,并 输⼊像 Janis Joplin 这样的姓名。不过这太烦琐了。所幸 pytest 提供了⼀ 种⾃动测试函数输出的⾼效⽅式。

2.1 单元测试和测试用例

测试⽤例(test case)是⼀组单元测试, 这些单元测试⼀道核实函数在各种情况下的⾏为都符合要求。良好的测试 ⽤例考虑到了函数可能收到的各种输⼊,包含针对所有这些情况的测试。

全覆盖(full coverage)测试⽤例包含⼀整套单元测试,涵盖了各种可能的 函数使⽤⽅式。

2.2 可通过的测试

使⽤ pytest 进⾏测试,会让单元测试编写起来⾮常简单。我们将编写⼀ 个测试函数,它会调⽤要测试的函数,并做出有关返回值的断⾔。如果断 ⾔正确,表⽰测试通过;如果断⾔不正确,表⽰测试未通过。

这个针对 get_formatted_name() 函数的测试如下:

from name_function import get_formatted_name
❶ def test_first_last_name():"""能够正确地处理像 Janis Joplin 这样的姓名吗?"""
❷ formatted_name = get_formatted_name('janis', 'joplin')
❸ assert formatted_name == 'Janis Joplin'

测试⽂件的名称很重要,必须以 test_打头。当你让 pytest 运⾏测试时,它将查找以 test_打头的⽂件,并 运⾏其中的所有测试。

在这个测试⽂件中,⾸先导⼊要测试的get_formatted_name() 函数。 然后,定义⼀个测试函数 test_first_last_name()(⻅❶)。

这个函 数名⽐以前使⽤的都⻓,原因有⼆。

第⼀,测试函数必须以 test_ 打头。在测试过程中,pytest 将找出并运⾏所有以 test_ 打头的函数。

第⼆, 测试函数的名称应该⽐典型的函数名更⻓,更具描述性。你⾃⼰不会调⽤ 测试函数,⽽是由 pytest 替你查找并运⾏它们。因此,测试函数的名称 应⾜够⻓,让你在测试报告中看到它们时,能清楚地知道它们测试的是哪 些⾏为

接下来,调⽤要测试的函数(⻅❷)。像运⾏ names.py 时⼀样,这⾥在调 ⽤ get_formatted_name() 函数时向它传递了实参 'janis' 和 'joplin'。将这个函数的返回值赋给变量 formatted_name。

最后,做出⼀个断⾔(⻅❸)。断⾔(assertion)就是声称满⾜特定的条 件:这⾥声称 formatted_name 的值为 'Janis Joplin'。

2.3 运行测试

打开⼀个终端窗⼝,并切换到这个测试⽂件所在的⽂件夹。如果你

使⽤的是 VS Code,可打开测试⽂件所在的⽂件夹,并使⽤该编辑器内嵌

的终端。在终端窗⼝中执⾏命令 pytest,你将看到如下输出:

$ pytest ========================= test session starts 
========================= 
❶ platform darwin -- Python 3.x.x, pytest-7.x.x, pluggy-1.x.x 
❷ rootdir: /.../python_work/chapter_11 
❸ collected 1 item 
❹ test_name_function.py . 
[100%] ========================== 1 passed in 0.00s 
==========================

⼀些有关运⾏测试的系统的 信息(⻅❶)。该测试是从哪个⽬录运⾏的(⻅❷)pytest 找到了⼀个测试(⻅❸),并 指出了运⾏的是哪个测试⽂件(⻅❹)

注意:如果出现⼀条消息,提⽰没有找到命令 pytest,请执⾏命 令 python -m pytest

2.4 未通过的测试

修改

get_formatted_name(),使其能够处理中间名,但同时故意让这个函

数⽆法正确地处理像 Janis Joplin 这样只有名和姓的姓名。

下⾯是 get_formatted_name() 函数的新版本,它要求通过⼀个实参指

定中间名:

def get_formatted_name(first, middle, last): """⽣成格式规范的姓名""" 
full_name = f"{first} {middle} {last}" return full_name.title()

对其进⾏测试时,我

们发现它不再能正确地处理只有名和姓的姓名了。

这次运⾏ pytest 时,输出如下:

 $ pytest ========================= test session starts 
========================= 
--snip-- 
❶ test_name_function.py F 
[100%] 
❷ ============================== FAILURES 
=============================== 
❸ ________________________ test_first_last_name 
_________________________ def test_first_last_name(): """能够正确地处理像 Janis Joplin 这样的姓名吗?""" 
❹ > formatted_name = get_formatted_name('janis', 'joplin') 
❺ E TypeError: get_formatted_name() missing 1 required positional argument: 'last' test_name_function.py:5: TypeError ======================= short test summary info 
======================= FAILED test_name_function.py::test_first_last_name - TypeError: get_formatted_name() missing 1 required positional argument: 
'last' ========================== 1 failed in 0.04s 
==========================

输出中有⼀个字⺟ F(⻅❶),表明有⼀个测试未通过。然后是 FAILURES 部分(⻅❷),这是关注的焦点,因为在运⾏测试时,通常应 该关注未通过的测试。接下来,指出未通过的测试函数是 test_first_last_name()(⻅❸)。右尖括号(⻅❹)指出了导致测 试未能通过的代码⾏。下⼀⾏中的 E(⻅❺)指出了导致测试未通过的具体 错误:缺少必不可少的位置实参 'last',导致 TypeError。在末尾的简 短⼩结中,再次列出了最重要的信息。

2.5 解决测试未通过

如果检查的条件没错,那么测试通过意味 着函数的⾏为是对的,⽽测试未通过意味着你编写的新代码有错。因此, 在测试未通过时,不要修改测试。因为如果你这样做,即便能让测试通 过,像测试那样调⽤函数的代码也将突然崩溃。相反,应修复导致测试不 能通过的代码:检查刚刚对函数所做的修改,找出这些修改是如何导致函 数⾏为不符合预期的。

在这个⽰例中,新增的中间名参数是必不可少 的。就这⾥⽽⾔, 最佳的选择是让中间名变为可选的。

def get_formatted_name(first, last, middle=''): """⽣成格式规范的姓名""" 
if middle: 
full_name = f"{first} {middle} {last}" 
else: 
full_name = f"{first} {last}" return full_name.title()

2.6 添加新测试

确定 get_formatted_name() ⼜能正确地处理简单的姓名后,我们再编 写⼀个测试,⽤于测试包含中间名的姓名。为此,在⽂件 test_name_function.py 中添加⼀个测试函数:

 from name_function import get_formatted_namedef test_first_last_name():--snip--def test_first_last_middle_name():"""能够正确地处理像 Wolfgang Amadeus Mozart 这样的姓名吗?"""
❶ formatted_name = get_formatted_name('wolfgang', 'mozart', 'amadeus')
❷ assert formatted_name == 'Wolfgang Amadeus Mozart'

为测试 get_formatted_name() 函数,我们先使⽤名、姓和中间名调⽤ 它(⻅❶),再断⾔返回的姓名与预期的姓名(名、中间名和姓)⼀致 (⻅❷)。再次运⾏ pytest,两个测试都通过了:

 $ pytest ========================= test session starts 
========================= 
--snip-- collected 2 items 
❶ test_name_function.py .. 
[100%] ========================== 2 passed in 0.01s 
==========================

3、测试类

如果针 对类的测试通过了,你就能确信对类所做的改进没有意外地破坏其原有的 ⾏为。

3.1 各种断言

到⽬前为⽌,我们只介绍了⼀种断⾔:声称⼀个字符串变量取预期的值。 在编写测试时,可做出任何可表⽰为条件语句的断⾔。如果该条件确实成 ⽴,你对程序⾏为的假设就得到了确认,可以确信其中没有错误。测试中常⽤的断⾔语句

断⾔

⽤途

assert a == b

断⾔两个值相等

assert a != b

断⾔两个值不等

assert a

断⾔ a 的布尔求值为 True

assert not a

断⾔ a 的布尔求值为 False

assert element in list

断⾔元素在列表中

assert element not in list

断⾔元素不在列表中

这⾥列出的只是九⽜⼀⽑,测试能包含任意可⽤条件语句表⽰的断⾔。

3.2 一个测试的类

类的测试与函数的测试相似,所做的⼤部分⼯作是测试类中⽅法的⾏为。

这是⼀

个帮助管理匿名调查的类:

# survey.py class AnonymousSurvey: """收集匿名调查问卷的答案""" 
❶ def __init__(self, question): """存储⼀个问题,并为存储答案做准备""" self.question = question self.responses = [] 
❷ def show_question(self): """显⽰调查问卷""" print(self.question) 
❸ def store_response(self, new_response): """存储单份调查答卷""" self.responses.append(new_response) 
❹ def show_results(self): """显⽰收集到的所有答卷""" print("Survey results:") for response in self.responses: print(f"- {response}")

这个类⾸先存储⼀个调查问题(⻅❶),并创建了⼀个空列表,⽤于存储 答案。这个类包含打印调查问题的⽅法(⻅❷),在答案列表中添加新答 案的⽅法(⻅❸),以及将存储在列表中的答案打印出来的⽅法(⻅❹)。 要创建这个类的实例,只需提供⼀个问题即可。编写⼀个使⽤它的程 序:

# language_survey.py 
from survey import AnonymousSurvey 
# 定义⼀个问题,并创建⼀个表⽰调查的 AnonymousSurvey 对象 
question = "What language did you first learn to speak?" 
language_survey = AnonymousSurvey(question) 
# 显⽰问题并存储答案 
language_survey.show_question() 
print("Enter 'q' at any time to quit.\n") 
while True: response = input("Language: ") if response == 'q': break language_survey.store_response(response) 
# 显⽰调查结果 
print("\nThank you to everyone who participated in the survey!") 
language_survey.show_results()

3.3 测试AnonymousSurvey类

下⾯来编写⼀个测试,对 AnonymousSurvey 类的⾏为的⼀个⽅⾯进⾏验 证。

# test_survey.py from survey import AnonymousSurvey 
❶ def test_store_single_response(): """测试单个答案会被妥善地存储""" question = "What language did you first learn to speak?" 
❷ language_survey = AnonymousSurvey(question) language_survey.store_response('English') 
❸ assert 'English' in language_survey.responses

⾸先,导⼊要测试的 AnonymousSurvey 类。第⼀个测试函数验证:调查

问题的单个答案被存储后,它会包含在调查结果列表中。对于这个测试函

数,⼀个不错的描述性名称是 test_store_single_response()(⻅

❶)。如果这个测试未通过,我们就能通过测试⼩结中的函数名得知,在 存储单个调查答案⽅⾯存在问题。 要测试类的⾏为,需要创建其实例。在❷处,使⽤问题"What language did you first learn to speak?" 创建⼀个名language_survey 的实例,然后使⽤ store_response() ⽅法存储单个答案 English。接下来,通过断⾔ English 在列表

language_survey.responses 中,核实这个答案被妥善地存储了(⻅ ❸)。

$ pytest test_survey.py
========================= test session starts =========================
--snip--
test_survey.py . [100%]
========================== 1 passed in 0.01s ==========================

。下⾯来核实,当 ⽤户提供三个答案时,它们都将被妥善地存储。为此,再添加⼀个测试函 数:

from survey import AnonymousSurvey def test_store_single_response(): 
--snip-- 
def test_store_three_responses(): 
"""测试三个答案会被妥善地存储""" 
question = "What language did you first learn to speak?" 
language_survey = AnonymousSurvey(question) 
❶responses = ['English', 'Spanish', 'Mandarin'] 
for response in responses: 
language_survey.store_response(response) 
❷for response in responses: 
assert response in language_survey.responses 

我们将这个新函数命名为 test_store_three_responses(),并像 test_store_single_response() ⼀样,在其中创建⼀个调查对象。 先定义⼀个包含三个不同答案的列表(⻅❶),再对其中的每个答案都调⽤ store_response()。存储这些答案后,使⽤⼀个循环来断⾔每个答案 都包含在 language_survey.responses 中(⻅❷)。 再次运⾏这个测试⽂件,两个测试(针对单个答案的测试和针对三个答案 的测试)都通过了:

$ pytest test_survey.py 
========================= test session starts ========================= 
--snip-- 
test_survey.py .. [100%] 
========================== 2 passed in 0.01s ========================== 

前述做法的效果很好,但这些测试有重复的地⽅。下⾯使⽤ pytest 的另 ⼀项功能来提⾼效率。

3.4 使用夹具

在测试中,夹具(fixture)可帮助我们搭建测试环境。这通常意味着创建供 多个测试使⽤的资源。在 pytest 中,要创建夹具,可编写⼀个使⽤装饰 器 @pytest.fixture 装饰的函数。装饰器(decorator)是放在函数定义 前⾯的指令。在运⾏函数前,Python 将该指令应⽤于函数,以修改函数代 码的⾏为。下⾯使⽤夹具创建⼀个 AnonymousSurvey 实例,让 test_survey.py 中的两 个测试函数都可使⽤它:

import pytest from survey import AnonymousSurvey 
❶ @pytest.fixture 
❷ def language_survey(): 
"""⼀个可供所有测试函数使⽤的 AnonymousSurvey 实例"""question = "What language did you first learn to speak?" 
language_survey = AnonymousSurvey(question) 
return language_survey 
❸ def test_store_single_response(language_survey): """测试单个答案会被妥善地存储""" 
❹ language_survey.store_response('English') assert 'English' in language_survey.responses 
❺ def test_store_three_responses(language_survey): """测试三个答案会被妥善地存储""" responses = ['English', 'Spanish', 'Mandarin'] for response in responses: 
❻ language_survey.store_response(response) for response in responses: assert response in language_survey.responses

现在需要导⼊ pytest,因为我们使⽤了其中定义的⼀个装饰器。我们将装 饰器@pytest.fixture(⻅❶)应⽤于新函数language_survey() (⻅❷)。这个函数创建并返回⼀个AnonymousSurvey 对象。 请注意,两个测试函数的定义都变了(⻅❸和❺):都有⼀个名为 anguage_survey 的形参。当测试函数的⼀个形参与应⽤了装饰器

@pytest.fixture 的函数(夹具)同名时,将⾃动运⾏夹具,并将夹具 返回的值传递给测试函数。在这个⽰例中,language_survey() 函数向 test_store_single_response() 和

test_store_three_responses() 提供了⼀个 language_survey 实 例。 两个测试函数都没有新增代码,⽽且都删除了两⾏代码(⻅❹和❻):定义 问题的代码⾏,以及创建 AnonymousSurvey 对象的代码⾏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux命令200例:dip用于用户与远程主机建立通信连接

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌。CSDN专家博主,阿里云社区专家博主,2023年6月csdn上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师&#xff0…

主题教育活动知识竞赛小程序界面分享

主题教育活动知识竞赛小程序界面分享

数据结构:数组

文章目录 数组一,概述二,添加数据三,删除数据 数组 一,概述 数组是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。 数组可以被视为一块连续的内存,即使物理上不连续&#xf…

微服务保护-授权规则

个人名片: 博主:酒徒ᝰ. 个人简介:沉醉在酒中,借着一股酒劲,去拼搏一个未来。 本篇励志:三人行,必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》,SpringCloud…

lighttpd以及socket和WebSocket编程

综述 本文涉及到下图绿色背景部分的内容: 左侧位于Linux下,其中包括lighttpd和socket程序;右侧是WebSocket程序。两者通过网络交互。 本文介绍lighttpd的基本使用方式,并通过编程完成一个socket服务器与浏览器端的WebSocket客户…

本地docker注册证书docker login连接到harbor仓库、利用shell脚本将大量镜像pull、tag、push到私有harbor仓库

1. 本地docker注册证书docker login连接到harbor仓库: 我们使用docker login/push/pull去与Harbor打交道,上传下载镜像等。 但是可能会出现x509: certificate signed by unknown authority之类的错误。 [roottest01 harbor.dev]# docker login harbor.d…

递归算法实现二分查找c++

递归算法可以用来实现二分查找。二分查找是一种高效的搜索算法&#xff0c;适用于已排序的数组。下面是使用递归算法实现二分查找的示例代码&#xff1a; #include <iostream> using namespace std;// 递归二分查找函数 int binarySearch(int arr[], int low, int high,…

数学建模__动态规划

动态规划就是&#xff0c;将任务每一步均记录下来&#xff0c;以便将来重复使用时能够直接调用 问题描述&#xff1a;给定n个物品&#xff0c;每个物品的重量是Wi,价值是Vi&#xff0c;但是背包最多能装下capacity重量的物品&#xff0c;问我们如何选择才能利益最大化。 这里涉…

Oracle的 dblink 学习笔记

文章目录 一、基础环境二、适用场景三、过程和方法四、参考资料 版权声明&#xff1a;本文为CSDN博主「杨群」的原创文章&#xff0c;遵循 CC 4.0 BY-SA版权协议&#xff0c;于2023年9月10日首发于CSDN&#xff0c;转载请附上原文出处链接及本声明。 原文链接&#xff1a;http…

Python | 根据子列表中的第二个元素对列表进行排序

在本文中&#xff0c;我们将学习如何根据主列表中存在的子列表的第二个元素对任何列表进行排序。 比如 Input : [[‘rishav’, 10], [‘akash’, 5], [‘ram’, 20], [‘gaurav’, 15]] Output : [[‘akash’, 5], [‘rishav’, 10], [‘gaurav’, 15], [‘ram’, 20]] Input …

信息化发展47

需求分析 1 、软件需求是指用户对新系统在功能、行为、性能、设计约束等方面的期望。 2 、根据IEEE 的软件工程标准词汇表&#xff0c; 软件需求是指用户解决问题或达到目标所需的条件或能力&#xff0c; 是系统或系统部件要满足合同、标准、规范或其他正式规定文档所需具有的…

Spring Framework介绍

什么是Spring Framework&#xff1f; Spring Framework 是一个开源的、轻量级的应用程序开发框架&#xff0c;它提供了一系列的解决方案&#xff0c;用于构建企业级Java应用程序。Spring Framework基于IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;和…

C#开发的OpenRA游戏之调试菜单1

C#开发的OpenRA游戏之调试菜单1 在开发一个游戏里,经常需要提供一些调试设置,以便快速地达到需要测试的阶段,否则按正常游戏的进程,就会需要比较久的时间,这样浪费开发人员的时间。 在OpenRA提供一个调试菜单,它就是在下面的界面里: 这个菜单叫做 Debug Menu,当玩家点…

抽象类和接口

目录 抽象类 接口 基本概念 多接口使用 为什么接口解决了Java的多继承问题&#xff1f; 接口的继承 克隆 Clonable接口 拷贝 Object类 抽象类 1.使用abstract修饰的方法称为抽象方法 2.使用abstract修饰的类称为抽象类 3.抽象类不可以被实例化 e.g.Shape shape ne…

ARMv7系统寄存器

本文档介绍 NEON 和 VFP 的系统寄存器,他们用于描述 NEON 和 VFP 的系统架构。包括:FPSCR, FPEXC, FPSID,MVFR0/1. 常见的术语 CPSR(Current Program Status Register)是 ARM 处理器中的一个寄存器,用于存储当前程序的状态信息。它包含了一些标志位,用于记录程序的运…

用户权限数据转换为用户组列表(3/3) - Excel PY公式

最近Excel圈里的大事情就是微软把PY塞进了Excel单元格&#xff0c;可以作为公式使用&#xff0c;轻松用PY做数据分析。系好安全带&#xff0c;老司机带你玩一把。 实例需求&#xff1a;如下是AD用户的列表,每个用户拥有该应用程序的只读或读写权限&#xff0c;现在需要创建新的…

QT之QLineEdit简介

QT之QLineEdit简介 QLineEdit 是 Qt 的一个类&#xff0c;用于创建和管理单行的文本输入框。以下是一些常用的 QLineEdit 函数和它们的用法&#xff1a; 1&#xff09;QLineEdit(QWidget *parent nullptr) 构造函数&#xff0c;创建一个新的 QLineEdit 对象。parent 参数是指…

【CSS】font-weight设置为500显示不出加粗效果

问题出在操作系统上&#xff1a; macOS 系统默认的华文黑体(STHeiti) 有七个矢量级别&#xff1a;Heavy/Bold/MediumP4/Regular/Thin/Light/UltraLightP2 &#xff0c;它包含上面 CSS 中设定的 500 这个精度。 Windows 系统默认的宋体(simsun) 没有那么多级别。在缺少级别支持…

23、mysql数据库的安装

&#xff08;无图&#xff0c;简易版本&#xff09; 一、下载 点开下面的链接&#xff1a;https://dev.mysql.com/downloads/mysql/ 点击Download 就可以下载对应的安装包了 二、解压 下载完成后我们得到的是一个压缩包&#xff0c;将其解压&#xff0c;我们就可以得到MyS…

李开复:我家的AI是坠吼的

创新工场董事长、鸿海独立董事李开复&#xff0c;近日出席鸿海股东会暨媒体记者会时表示&#xff0c;人工智能&#xff08;AI&#xff09;是人类史上即将面临的最伟大技术革命&#xff0c;未来十年的改变将改写人类历史、重构所有产业&#xff0c;其发展大致可分三阶段&#xf…