掌握 Python 异常处理的实战技巧:从基础到高级应用20240918
掌握 Python 异常处理的实战技巧:从基础到高级应用
引言
在 Python 编程中,异常处理是保障代码稳健性和可靠性的关键要素之一。无论是在网络请求、资源访问,还是复杂的业务逻辑中,异常处理都不可或缺。本文将从 Python 异常的基础知识入手,深入探讨如何在实战中有效地捕获和处理异常,并探讨自定义异常的重要性和实现方法。通过最佳实践的示例,帮助您提升异常处理的技能,编写出更加健壮的代码。
Python 异常的层次结构
Python 中的异常都是继承自 BaseException
类的。BaseException
是所有异常的基类,常见的异常类型分支如下:
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception├── ArithmeticError├── LookupError├── ValueError├── TypeError├── ...(其他内建异常)└── ...(用户自定义异常)
BaseException
:所有异常的根类,不应直接继承自此类。SystemExit
、KeyboardInterrupt
、GeneratorExit
:这些是系统级的异常,一般不需要用户捕获。Exception
:常用的异常基类,大多数自定义异常都应该继承自它。
Python 异常处理的格式
Python 提供了一个结构化的方式来处理异常,基本格式如下:
try:# 可能会抛出异常的代码
except (Exception1, Exception2) as e:# 捕获指定的异常并处理
else:# 如果没有发生异常,执行这部分代码
finally:# 无论是否发生异常,都会执行的代码(通常用于资源释放)
各部分详解
try
块:包含可能引发异常的代码。如果没有异常发生,程序会跳过except
块,执行else
块(如果存在)。except
块:用于捕获并处理指定的异常类型。可以有多个except
块,分别处理不同类型的异常。else
块:仅当try
块中没有发生任何异常时执行。这部分代码用于在成功执行try
块后,需要额外执行的操作。finally
块:无论是否发生异常,都会执行的代码。通常用于释放资源,如关闭文件、网络连接等。
注意事项
- 未被捕获的异常不会进入
else
块:如果在try
块中发生了异常,且该异常未被任何except
块捕获,else
块将被跳过,异常会继续向上传播。 finally
块总是会执行:无论是否发生异常,finally
块的代码都会被执行,适用于资源的清理和释放。
示例:详细理解异常处理格式
def divide(a, b):try:print("进入 try 块")result = a / bexcept ZeroDivisionError as e:print(f"捕获到 ZeroDivisionError:{e}")except TypeError as e:print(f"捕获到 TypeError:{e}")else:print("没有发生异常,执行 else 块")print(f"计算结果是:{result}")finally:print("执行 finally 块,无论是否发生异常")# 示例调用
print("测试 a=10, b=2:")
divide(10, 2)print("\n测试 a=10, b=0:")
divide(10, 0)print("\n测试 a=10, b='a':")
divide(10, 'a')
输出:
测试 a=10, b=2:
进入 try 块
没有发生异常,执行 else 块
计算结果是:5.0
执行 finally 块,无论是否发生异常测试 a=10, b=0:
进入 try 块
捕获到 ZeroDivisionError:division by zero
执行 finally 块,无论是否发生异常测试 a=10, b='a':
进入 try 块
捕获到 TypeError:unsupported operand type(s) for /: 'int' and 'str'
执行 finally 块,无论是否发生异常
解析:
-
第一次调用
divide(10, 2)
:try
块中没有发生异常。- 跳过所有的
except
块。 - 执行
else
块,输出计算结果。 - 执行
finally
块。
-
第二次调用
divide(10, 0)
:try
块中发生ZeroDivisionError
。- 进入对应的
except ZeroDivisionError
块,处理异常。 - 跳过
else
块。 - 执行
finally
块。
-
第三次调用
divide(10, 'a')
:try
块中发生TypeError
。- 进入对应的
except TypeError
块,处理异常。 - 跳过
else
块。 - 执行
finally
块。
关于未被捕获的异常
如果在 try
块中发生了未被 except
块捕获的异常,那么:
else
块将被跳过。finally
块仍然会执行。- 异常将继续向上传播,直到被捕获或导致程序崩溃。
示例:未捕获的异常
def test_unhandled_exception():try:print("进入 try 块")result = undefined_variable # 未定义的变量,触发 NameErrorexcept ZeroDivisionError as e:print(f"捕获到 ZeroDivisionError:{e}")else:print("没有发生异常,执行 else 块")finally:print("执行 finally 块,无论是否发生异常")# 示例调用
test_unhandled_exception()
输出:
进入 try 块
执行 finally 块,无论是否发生异常
Traceback (most recent call last):File "example.py", line 12, in <module>test_unhandled_exception()File "example.py", line 4, in test_unhandled_exceptionresult = undefined_variable # 未定义的变量,触发 NameError
NameError: name 'undefined_variable' is not defined
解析:
try
块中发生了NameError
,但没有对应的except
块捕获该异常。else
块被跳过。finally
块执行。- 异常向上传播,导致程序崩溃并打印堆栈跟踪信息。
建议
- 明确捕获异常类型:尽量只捕获您预期的异常,避免使用过于宽泛的
except Exception
,以免掩盖其他潜在的问题。 - 处理未捕获的异常:如果需要,可以在最外层添加一个通用的异常处理,或者使用全局异常处理器,确保程序的健壮性。
- 测试代码逻辑:通过单元测试和异常测试,确保在各种情况下程序都能按预期运行。
常见的异常处理场景
在实际开发中,异常处理常见于以下场景:
- 网络请求:处理超时、连接错误等网络异常。
- 资源访问:解决文件不存在、权限不足等问题。
- 代码逻辑:捕获越界访问、
KeyError
等逻辑错误。
示例:处理网络请求中的异常
import requestsdef fetch_data(url):try:response = requests.get(url, timeout=5)response.raise_for_status()except requests.exceptions.Timeout as e:print(f"请求超时:{e}")except requests.exceptions.HTTPError as e:print(f"HTTP 错误:{e}")except requests.exceptions.RequestException as e:print(f"发生网络错误:{e}")else:print("请求成功,处理响应数据。")return response.json()finally:print("请求结束。")# 示例调用
data = fetch_data("https://api.example.com/data")
在这个示例中,我们通过 try-except-else-finally
结构来处理网络请求中的各种潜在问题,如超时和其他网络错误。
为什么需要自定义异常?
在实际的开发中,标准库提供的异常类型并不能涵盖所有的业务需求。这时,自定义异常便显得尤为重要。通过自定义异常,您可以:
- 表达特定的业务逻辑错误。
- 附加更多的上下文信息,便于调试。
- 提高代码的可读性和维护性。
如何自定义异常?
自定义异常通常是继承自 Exception
类,而非 BaseException
,这是因为 BaseException
包含了一些系统退出等行为,不适合普通业务逻辑使用。
示例:自定义异常
class MyBusinessError(Exception):def __init__(self, message, error_code):super().__init__(message)self.error_code = error_codedef process_data(data):if not isinstance(data, dict):raise MyBusinessError("数据格式无效", 1001)# 处理数据的逻辑if 'key' not in data:raise MyBusinessError("缺少必要的键:'key'", 1002)try:process_data("Invalid data")
except MyBusinessError as e:print(f"发生错误:{e},错误代码:{e.error_code}")
在此示例中,我们定义了一个 MyBusinessError
异常类,能够携带额外的错误代码,用于更加精准的错误处理。
拓展内容
1. 使用上下文管理器处理资源
Python 提供了 with
语句,用于管理资源的分配和释放。上下文管理器通过实现 __enter__
和 __exit__
方法,能够确保资源在使用后得到正确释放,避免资源泄漏的问题。
示例:使用上下文管理器处理文件操作
class FileManager:def __init__(self, filename, mode):self.filename = filenameself.mode = modeself.file = Nonedef __enter__(self):print("打开文件")self.file = open(self.filename, self.mode)return self.filedef __exit__(self, exc_type, exc_value, traceback):print("关闭文件")if self.file:self.file.close()if exc_type:print(f"发生错误:{exc_value}")return True # 抑制异常# 示例调用
with FileManager('example.txt', 'w') as f:f.write('Hello, world!')# 手动引发异常进行测试# raise ValueError("测试异常")
通过上下文管理器,我们可以简化资源管理,并在必要时捕获和处理异常。
2. 日志记录与异常处理
在生产环境中,仅仅捕获异常是不够的,记录异常信息对于排查问题至关重要。Python 的 logging
模块提供了一种灵活的方式来记录异常,支持将日志输出到控制台、文件或远程服务器。
示例:使用日志记录异常
import logginglogging.basicConfig(level=logging.ERROR,format='%(asctime)s %(levelname)s %(message)s',filename='app.log')def divide(a, b):try:return a / bexcept ZeroDivisionError as e:logging.error(f"尝试除以零:{e}")raise # 重新引发异常# 示例调用
try:divide(10, 0)
except ZeroDivisionError:print("捕获到一个异常!")
通过 logging
模块,我们不仅可以记录异常,还能保留详细的错误信息,以便后续分析。
3. 异常的链式处理
在处理异常时,有时需要保留原始异常的上下文信息,以便更好地理解错误的发生原因。Python 允许使用 raise ... from ...
语句来链式引发异常,从而保留原始异常的堆栈信息。
深入解析
当一个异常发生时,我们可能希望捕获它,然后抛出一个新的异常,但又不想丢失原始异常的信息。raise ... from ...
语句可以帮助我们实现这一点,它明确地将新的异常与原始异常关联起来。
示例:链式处理异常
class DataProcessingError(Exception):"""数据处理错误的自定义异常"""passdef process_data(data):try:result = int(data)except ValueError as e:# 使用 'from' 保留原始异常信息raise DataProcessingError("数据处理失败,无法转换为整数") from eelse:return result * 2# 示例调用
try:process_data("invalid")
except DataProcessingError as e:print(f"发生错误:{e}")# 通过 '__cause__' 属性访问原始异常print(f"原始异常:{e.__cause__}")
输出:
发生错误:数据处理失败,无法转换为整数
原始异常:invalid literal for int() with base 10: 'invalid'
应用场景
- 增加错误的语义化:使异常信息更贴近业务逻辑。
- 调试复杂问题:保留异常链有助于追踪问题的根源。
- 错误封装:在模块内部使用内部异常类型,对外部提供统一的异常接口。
4. 提高代码健壮性的最佳实践
在异常处理上,还有一些通用的最佳实践可以帮助提高代码的健壮性:
- 避免过度捕获:不要过度捕获所有异常,尤其是使用
except Exception:
时。应尽可能捕获特定的异常,以免掩盖潜在的问题。 - 详细的错误信息:在抛出或记录异常时,尽量提供有用的上下文信息,帮助快速定位问题。
- 统一的异常处理策略:在大型项目中,使用统一的异常处理策略或中间件,确保异常处理的一致性。
5. 异常和单元测试
在编写单元测试时,测试代码是否能够正确处理异常是非常重要的。unittest
和 pytest
都提供了对异常进行测试的功能。
示例:使用 unittest
测试异常
import unittestdef divide(a, b):if b == 0:raise ValueError("不能除以零")return a / bclass TestDivision(unittest.TestCase):def test_divide_by_zero(self):with self.assertRaises(ValueError) as context:divide(10, 0)self.assertEqual(str(context.exception), "不能除以零")if __name__ == '__main__':unittest.main()
通过测试异常,我们可以确保代码在异常情况下的行为符合预期,从而提高代码的健壮性。
6. 全局异常处理
在某些应用程序中,您可能希望设置一个全局的异常处理器,捕获所有未处理的异常,避免应用程序崩溃。这个策略通常在 Web 应用程序或者 GUI 应用程序中非常有用。
深入解析
Python 提供了 sys.excepthook
函数,允许我们定义未捕获异常的处理方式。默认情况下,未捕获的异常会导致程序终止,并在控制台输出堆栈跟踪信息。通过自定义 sys.excepthook
,我们可以控制异常的输出形式,或者在异常发生时执行特定的操作,例如日志记录、资源清理等。
示例:全局异常处理器
import sys
import tracebackdef global_exception_handler(exc_type, exc_value, exc_traceback):if issubclass(exc_type, KeyboardInterrupt):# 对于键盘中断,调用默认的异常处理sys.__excepthook__(exc_type, exc_value, exc_traceback)return# 打印自定义的错误信息print("捕获到未处理的异常:")print(f"类型:{exc_type.__name__}")print(f"值:{exc_value}")# 可选择将堆栈信息写入日志with open('error.log', 'a') as f:traceback.print_exception(exc_type, exc_value, exc_traceback, file=f)# 也可以在此处添加其他清理或通知操作# 设置全局异常处理器
sys.excepthook = global_exception_handler# 示例引发未捕获的异常
def faulty_function():return 1 / 0faulty_function()
输出:
捕获到未处理的异常:
类型:ZeroDivisionError
值:division by zero
注意事项
- 谨慎使用全局异常处理器:全局异常处理器可能会掩盖程序中的严重错误,导致问题难以被发现。
- 避免吞掉异常:在处理异常时,应确保记录足够的信息,并在必要时让程序继续抛出异常。
- 线程中的异常:对于多线程程序,线程中的异常不会触发
sys.excepthook
,需要使用threading.excepthook
(Python 3.8 及以上)或者在线程中手动捕获异常。
应用场景
- 日志记录:统一捕获未处理的异常,记录日志,便于问题排查。
- 用户反馈:在 GUI 应用中,捕获异常后给用户友好的提示,而不是程序崩溃。
- 资源清理:在异常发生时执行必要的资源释放或状态恢复操作。
7. 在 GUI 应用中使用全局异常处理
import sys
import tkinter as tk
from tkinter import messageboxdef global_exception_handler(exc_type, exc_value, exc_traceback):# 显示错误对话框messagebox.showerror("错误", f"发生未处理的异常:\n{exc_value}")# 可以在此处记录日志或执行其他操作sys.excepthook = global_exception_handler# 创建一个简单的 GUI 应用
def create_gui():root = tk.Tk()root.title("异常处理示例")def cause_exception():# 引发一个异常raise ValueError("这是一个示例异常")btn = tk.Button(root, text="引发异常", command=cause_exception)btn.pack(padx=20, pady=20)root.mainloop()create_gui()
在这个示例中,当用户点击按钮时,会引发一个异常。全局异常处理器会捕获该异常,并显示一个错误对话框,而不是让程序崩溃退出。
结论
Python 的异常处理机制是编写健壮代码的基础。通过掌握异常的层次结构、异常处理的格式、常见处理场景、自定义异常,以及一些高级技巧,您可以应对各种复杂的实际问题。
在实践中,请务必遵循 try-except-else-finally
结构来确保代码的稳定性,并深入理解各个代码块的执行顺序和条件。明确 else
块只在没有发生任何异常时执行,而未被捕获的异常不会进入 else
块,这有助于避免逻辑混淆。
同时,通过自定义异常和日志记录增强代码的可维护性。理解和正确使用异常的链式处理和全局异常处理,可以帮助您编写出更加健壮和专业的应用程序。
无论是网络请求、文件操作还是复杂的业务逻辑,正确的异常处理都是不可或缺的技能。希望本文的深入解析和示例能够帮助您更好地掌握 Python 异常处理的实战技巧,提升您的编程能力。
相关文章:
掌握 Python 异常处理的实战技巧:从基础到高级应用20240918
掌握 Python 异常处理的实战技巧:从基础到高级应用 引言 在 Python 编程中,异常处理是保障代码稳健性和可靠性的关键要素之一。无论是在网络请求、资源访问,还是复杂的业务逻辑中,异常处理都不可或缺。本文将从 Python 异常的基…...
One API 部署与配置指南
技术文档:One API 部署与配置指南 概述 One API 是一个多功能的 API 管理平台,支持自定义设置、用户管理、多种登录注册方式、主题切换等。本文档提供了详细的部署和配置指南,帮助用户快速搭建和使用 One API。 部署 基于 Docker 部署 D…...
国密起步7:BouncyCastle使用SM4自定义格式加解密C#版
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 github源码指引的指引-CSDN博…...

Qt优秀开源项目之二十三:QSimpleUpdater
QSimpleUpdater是开源的自动升级模块,用于检测、下载和安装更新。 github地址:https://github.com/alex-spataru/QSimpleUpdater QSimpleUpdater目前Star不多(911个),但已在很多开源项目看到其身影,比如Not…...
使用 Nmap 进行 SSL/TLS 加密套件枚举
1. Nmap 简介 Nmap(Network Mapper)是一个开源的网络探测和安全审计工具。它广泛用于扫描网络并发现设备、端口及服务,同时也支持多种脚本来进行更高级的安全扫描。Nmap 的 -sV 参数可以用于探测开放端口上的服务及版本信息,配合…...

探索 Python 的火焰:Fire 库的神秘力量
文章目录 🔥 探索 Python 的火焰:Fire 库的神秘力量第一部分:背景介绍第二部分:Fire 库是什么?第三部分:如何安装 Fire?第四部分:简单库函数使用方法第五部分:场景应用第…...

【Day14-单例设计模式动态代理】
单例设计模式 什么是设计模式(Design pattern) ? 一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。设计模式有20多种,对应20多种软件开发中会遇到的问题…...
代码随想录训练营Day7 | 454.四数相加II | 383. 赎金信 | 15. 三数之和 | 18. 四数之和
代码随想录 (programmercarl.com) Leetcode 454. 四数相加 II 题目描述 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] B[j] C[k] D[l] 0。 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N&#…...

C++和OpenGL实现3D游戏编程【目录】
欢迎来到zhooyu的专栏。 个人主页:【zhooyu】 文章专栏:【OpenGL实现3D游戏编程】 贝塞尔曲面演示: 贝塞尔曲面演示zhooyu 本专栏内容: 我们从游戏的角度出发,用C去了解一下游戏中的功能都是怎么实现的。这一切还是要…...

03-Mac系统PyCharm主题设置
目录 1. 打开PyCharm窗口 2. Mac左上角点击PyCharm,点击Settings 3. 点击第一项Appearance& Behavior 4. 点击Appearance 5. 找到Theme进行设置 1. 打开PyCharm窗口 2. Mac左上角点击PyCharm,点击Settings 3. 点击第一项Appearance& Behavi…...
Java并发的四大定律
每一个进入 Java 并发世界的人,都会不可避免地面临一系列问题:线程安全、并发控制、锁,以及共享资源。这些概念复杂又抽象,往往让人无从下手。幸运的是,业界早已总结出一些法则,这些法则为我们处理并发问题…...

java项目之基于springboot的贸易行业crm系统(源码+文档)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的基于springboot的贸易行业crm系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于sp…...
General OCR Theory: Towards OCR-2.0 via a Unified End-to-end Model
摘要 传统的OCR系统(OCR-1.0)越来越无法满足人们对智能处理人造光学字符的需求。在本文中,我们将所有人造光学信号(例如,普通文本、数学/分子公式、表格、图表、乐谱,甚至是几何形状)统称为“字…...

二十种编程语言庆祝中秋节
二十种编程语言庆祝中秋节 文章目录 二十种编程语言庆祝中秋节中秋快乐!家人们 🥳一 Python二 C三 C四 Java五 C#六 Perl七 Go八 Asp九 PHP十 JavaScript十一 JavaScript HTML十二 Visual Basic十三 早期 VB十四 Visual C十五 Delphi十六 Shell十七 Cobo…...

202409012在飞凌的OK3588-C的核心板上使用Rockchip原厂的Buildroot点MIPI屏【背光篇】
202409012在飞凌的OK3588-C的核心板上使用Rockchip原厂的Buildroot点MIPI屏【背光篇】 2024/9/12 10:44 缘起,拿到一块MIPI屏,需要使用飞凌的OK3588-C的核心板在Android12下点亮。 在飞凌的Linux R4下修改部分屏参之后即可直接点亮。 但是在飞凌的Andro…...

DMDRS搭建
DMDRS搭建 本次进行DMDRS工具的部署搭建以及使用 环境配置 操作系统及数据库配置 操作系统:使用CentOS7数据库:dm8_20240408_x86_rh7_64 服务器配置 实例名服务器IPDM1192.168.19.7(源DMDRS)DM2192.168.19.4(目的…...

【油猴脚本】00006 案例 Tampermonkey油猴脚本自定义表格列名称,自定义表格表头,自定义表格的thead里的td
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【油…...

JS - 获取剪切板内容 Clipboard API
目录 1,需求最终效果 2,实现示例 3,注意点1,只支持安全上下文环境2,只能读取当前页面的剪切板3,权限获取问题4,获取内容的 MIME_TYPE 问题1,文本内容2,图片内容 5&#x…...
Qt自动打开文件夹并高亮文件
在Qt中,如果你想要打开一个文件夹并在文件管理器中高亮显示(选中)某个文件,你可以使用以下方法: 对于Windows系统,你可以使用QProcess来启动explorer命令,并带上/select,参数来高亮显示文件。以…...

神经网络-MNIST数据集训练
文章目录 一、MNIST数据集1.数据集概述2.数据集组成3.文件结构4.数据特点 二、代码实现1.数据加载与预处理2. 模型定义3. 训练和测试函数4.训练和测试结果 三、总结 一、MNIST数据集 MNIST数据集是深度学习和计算机视觉领域非常经典且基础的数据集,它包含了大量的手…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...