新闻详情

新闻详情

首页 / 资讯中心 / 详情

深入解析Python报错:‘NoneType‘ object does not support item assignment

发布时间:2026/9/30 15:45:20来源:尧图网络
深入解析Python报错:‘NoneType‘ object does not support item assignment
这个报错我太熟悉了几乎每个写过 Python 的人都会撞上它尤其是处理数据、调接口、写爬虫的时候冷不丁就给你来一句TypeError: NoneType object does not support item assignment。第一次看到这个报错的人大概率是懵的明明看起来是在给列表或者字典赋值怎么突然就冒出一个 NoneType这玩意儿到底是从哪儿来的简单说这句话的意思是你把一个本应是容器对象列表、字典等的东西当成了可赋值的目标但这个对象实际上是 None —— 空值。而 None 在 Python 里属于 NoneType 类型它既没有“下标”的概念也完全不支持“按索引/键赋值”的操作。所以一旦代码里出现obj[key] value或者obj[index] value而 obj 恰好是 None解释器就直接抛这个异常。这篇文章会把这个问题彻底讲透。我会从报错的三个关键字拆解开始然后用大量真实的场景复现、排查思路、修复方案最后再把我在实际项目中踩过的坑和总结的防御性写法一并分享出来。不管你是刚入门的小白还是写了两三年 Python 的老手只要你在跟 None、字典、列表、数据库查询结果打交道这篇都值得收藏。1. 这个报错到底在说什么1.1 拆解 TypeError、NoneType 与 item assignment先把这个报错的英文拆开看每个单词都对应一个明确的信息。TypeError是 Python 内置异常类型之一意思是“类型错误”。Python 是强类型语言不是说不能改变变量的类型而是说某个操作在当前这个对象的类型上根本不存在时就会抛 TypeError。比如整数不能调用字符串的方法None 不能被下标访问都属于类型层面的不匹配。NoneType指的是 None 这个对象的所属类型。在 Python 里None是一个单例对象表示“没有值”“空”。它的类型是NoneType这个类型有且仅有一个实例就是你每次写None时拿到的那个对象。要注意的是None 不等于 False不等于空字符串也不等于空列表。它就是一个独立的、代表“无”的存在。item assignment翻译过来是“条目赋值”或者“项赋值”指的就是对容器元素进行赋值操作。常见的写法有三类# 字典通过键赋值 d[key] value # 列表通过索引赋值 lst[0] value # 嵌套结构赋值 obj[outer][inner] value这三类操作的本质都是调用对象的__setitem__方法。而NoneType根本没有实现这个方法所以当你对 None 做上述操作时解释器就会抛出TypeError: NoneType object does not support item assignment。这个逻辑可以用生活里的场景来类比你想往一个抽屉里放东西但那个位置放的根本不是一个抽屉而是一堵墙。你拿着钥匙去开锁锁孔都不存在自然会失败。1.2 为什么这个报错经常出现在数据处理和接口调用中在实践中这个报错出现频率最高的场景就是数据处理和 API 调用。原因很直白只要某个函数没有按预期返回数据返回值就是 None。举个例子你写了一个函数逻辑是查询数据库并返回结果但查询没匹配到任何记录。如果你在函数里写的是def get_user(user_id): conn get_connection() cursor conn.execute(SELECT * FROM users WHERE id?, (user_id,)) user cursor.fetchone() # 注意这里没有做任何处理直接返回 return user当user_id不存在时fetchone()返回的就是 None。然后你在业务代码里写user get_user(10086) user[name] 张三第二行直接爆炸。因为user是 None而你却试图用字符串键去给它赋值。这类问题在数据量大的时候尤其隐蔽——大多数查询都有结果代码跑得好好的直到某天出现一条脏数据整个程序就崩了。再比如爬虫场景data response.json() data[result][list][0][title] 新标题只要response.json()返回的结构里缺了result这个键data[result]就是 None如果用的是.get()或者直接 KeyError如果用[]。一旦用[]访问 None 的下标同样会触发这个报错。这也就是为什么很多数据相关的代码都在强调“判空”。判空不是形式主义它是真正能避免这类崩溃的防线。2. 七种典型触发场景全复盘这一节我来还原几个真实的触发场景每个都是我在开发和排查过程中实际遇到过的或者是在 Stack Overflow 上看到的高频案例。我把它们整理成了可以直接对照的代码片段。2.1 场景一函数忘记写 return这是最基础也最容易犯的一种。看一下这段代码def build_config(): config {} config[host] localhost config[port] 3306 # 忘了写 return config config build_config() config[timeout] 30 # 这里抛 TypeError函数执行完之后没有返回任何值那么调用方拿到的就是 None。你后续在 None 上做config[timeout] 30的操作自然触发报错。这种问题在真实项目中比想象中更频繁尤其是在函数体比较长、逻辑分支比较多的时候。我见过有人写了二十多个 if-else 分支其中某个分支里忘了 return结果线上数据一走到那个分支就全线崩溃。排查的时候盯着代码看了半天才找到那个缺失的 return。修复方案def build_config(): config {} config[host] localhost config[port] 3306 return config config build_config() or {} config[timeout] 30这里的or {}是一种常见的兜底写法意思是如果返回 None就当成空字典处理。2.2 场景二使用了下标赋值的变量被重新赋值为 None再看这个场景。某个变量一开始是字典但在程序运行过程中被赋值为 None 了result {status: ok, data: {count: 1}} # ... 中间很多代码 ... if some_condition: result None # 某个逻辑把 result 覆盖了 # ... 继续处理 ... result[data] {count: 2} # 这里崩溃这种问题的隐蔽之处在于变量类型在运行时被改变而你没有察觉。尤其是当代码分散在多个函数、多个文件之间时很难一眼看出某个变量到底在哪里变成了 None。排查这种问题我推荐一个非常有效的方法在报错的那一行前面加上一行打印语句打印变量的类型print(type(result), result) result[data] {count: 2}只要看到输出里有class NoneType就能确认这个变量在此时已经是 None 了。然后顺着这个变量的赋值路径往回查就能找到罪魁祸首。2.3 场景三字典嵌套结构中缺少中间键这种场景在接口返回数据处理中极其常见。假设你请求了一个接口返回的 JSON 结构是这样的{ code: 0, data: { user: { profile: { name: 张三 } } } }你的代码是这样写的resp requests.get(url).json() resp[data][user][profile][age] 30正常情况下没问题。但某天接口做了一次调整user字段变成了 null那么resp[data][user]拿到的就是 None再往下执行[profile][age] 30直接触发TypeError: NoneType object does not support item assignment。这种情况下的报错信息其实相当具有迷惑性因为它指向的是最终那一行而不是真正出问题的那一层。真正的问题在于中间某个层级的值是 None。修复方案profile resp.get(data, {}).get(user, {}).get(profile, None) if profile is None: profile {} # 或者根据业务决定怎么处理 profile[age] 30我个人的习惯是对于复杂的嵌套 JSON先用json.dumps()把整个结构打印出来肉眼确认每一层实际返回了什么再下手写链式访问代码。2.4 场景四数据库查询结果为空用fetchone()或fetchall()查询数据库时如果没有匹配记录返回结果是 None 或空列表。不少初学者会默认查询结果一定存在然后直接对结果做赋值操作row cursor.fetchone() row[status] processed # 如果 row 是 None直接报错这种代码的危害不仅在于报错更在于它隐藏了业务逻辑上的漏洞当数据不存在时程序应该如何处理是插入一条新记录、跳过处理还是抛出更明确的业务异常我通常会在查询后立刻做判断row cursor.fetchone() if row is None: logging.warning(No record found for user_id%s, user_id) return # 或者走新建逻辑 row[status] processed这里有一个小技巧SQLite 的sqlite3.Row类型和 MySQL 的字典游标都支持通过键名访问列值但如果返回 None任何访问方式都救不了你。先判空是唯一正确的做法。2.5 场景五列表索引越界后的赋值列表的lst[i] value同样是 item assignment。当列表为空或者索引越界时你拿lst[0] 1操作如果 lst 是 None而不是空列表报错就是 NoneType 的报错如果 lst 是空列表报错则是 IndexError。两者不一样但同样踩过的人很多。一个经典例子def get_first_item(items): return items[0] first get_first_item(None) # 传入 None first[processed] True # 这里报 NoneType根本原因还是传参时传入了 None而函数内部没有校验。修复方式是加一个类型检查def get_first_item(items): if items is None: return None return items[0]进一步地调用方也要处理返回值first get_first_item(data_list) if first is not None: first[processed] True2.6 场景六第三方库返回 None调用第三方库时返回值可能受各种因素影响变成 None。比如用 OpenCV 读取图片路径错了的时候cv2.imread()返回的就是 Noneimg cv2.imread(/path/to/nonexistent.jpg) img[0, 0] (255, 255, 255) # 报错这种报错其实是第三库在提醒你你输入的数据有问题。但很多新手会以为是自己赋值写错了反复检查赋值语句却忽略了最前面的数据获取环节。经验是凡是调用外部依赖的函数都把返回值先检查一遍再往下走。这种习惯能帮你避开 90% 的类似问题。2.7 场景七字典初始化时使用dict()但最终变量没绑定看这段代码my_dict dict() # ... 中间有异常或者其他逻辑 ... # my_dict 没有被赋值或者被设置成 None my_dict[key] value # 报错本质与场景二类似属于变量状态被改变。这类问题在异步编程里尤其常见——你期望某个回调函数里给全局变量赋值但回调没有按预期触发导致后续代码拿到的还是 None。3. 定位问题的实战思路与方法下面进入排查层面的内容。当你在代码里撞上这个报错不要慌按这套流程走一般十分钟内能找到真凶。3.1 追踪报错堆栈锁准位置绝大多数情况下Python 的 traceback 会精确地告诉你哪一行报错。不要只盯着报错信息本身要往上看堆栈信息Traceback (most recent call last): File /path/to/script.py, line 12, in module config[timeout] 30 TypeError: NoneType object does not support item assignment这里的关键信息是文件路径script.py、行号12、操作代码config[timeout] 30。但要注意报错行只是“中了子弹的位置”不是“扣动扳机的位置”。真正的问题往往在更早的地方——config是在哪里被变成 None 的。所以你要做的是从第 12 行往上回溯找到config最后一次被赋值的地方。我的做法是先把 traceback 里涉及到的那个变量圈出来然后用编辑器的全局搜索功能VS Code 的CtrlShiftFPyCharm 的CtrlShiftR搜索这个变量名出现的所有位置逐一检查赋值路径。3.2 写最小复现代码如果代码逻辑太复杂直接看代码容易绕晕我的经验是写一个最小复现脚本把报错的最小条件抠出来单独验证。比如你怀疑是某个函数返回了 None那就直接写def suspicious_function(): # 拷贝原始函数的核心逻辑 ... result suspicious_function() print(result) print(type(result))运行这个脚本立刻就能知道这个函数到底返回了什么。比在大型项目里反复打断点效率高得多。3.3 使用断言或工具函数做快速检查在关键节点插入断言能快速拦截异常状态assert config is not None, config should not be None!如果 config 是 None程序会在这一行直接崩掉并且给出你自定义的提示信息。这个做法的好处是把错误信息变清晰了。原报错说的是“NoneType 不支持赋值”你的断言说的是“config 不应该为 None”——后者显然对你定位问题更有帮助。还有一种做法是写一个小工具函数def ensure_dict(data, namedata): if data is None: return {} if not isinstance(data, dict): raise TypeError(f{name} expect dict, got {type(data)}) return data调用方改成config ensure_dict(config, config) config[timeout] 30这样处理之后即使源头是 None也不会再触发原报错而是按逻辑兜底成空字典继续执行。3.4 IDE 断点调试静态检查没法定位的时候就用断点调试。在报错行以及这个变量出现的关键位置前打断点运行到断点时在 debugger 窗口里直接查看变量的当前值。比较实用的操作是在报错行前面打一个断点运行停住后在“Watches”窗口添加表达式type(config)就能立刻看到 config 的类型。然后继续往下看当前错误发生的上下文环境里config 到底是什么时候变成 None 的。我用 PyCharm 调试时喜欢开启“View as Array/Tuple”功能配合 Evaluate Expression 窗口能直接预览容器对象内部结构排查嵌套数据尤其方便。4. 防御性编程从根源上消灭这个报错排查只是治标真正靠谱的写代码方式是在源头杜绝 None 问题的发生。这一节聊几个有效的防御性手段。4.1 函数返回值约定不返回 None尽量返回空容器这是一个非常有效的设计约定让函数在“无结果”时返回空容器而不是 None。比如查询用户列表没有结果时返回[]而不是 None查询单个配置项没有结果时返回一个默认值或者抛明确的异常而不是返回 None 让调用方去猜。def get_user_list(): # 查询... if no_result: return [] # 而不是 return None return result_list这样调用方就可以直接遍历不用判空for user in get_user_list(): user[name] user[name].title()少一个 None就少一层判空。这不仅能消灭当前这个报错还能让代码整体可读性上升一个档次。我在重构老项目时经常会做这件事把所有“无结果时返回 None”的函数改成“返回空容器”顺手就能消灭一大批潜在 bug。4.2 使用字典的setdefault或defaultdict如果你经常需要向多层字典里写数据可以考虑使用defaultdict或者setdefault来避免中间层为 None 的问题。普通写法容易踩坑data {} data[user][name] 张三 # 报错dict object has no attribute name即使 data 不是 None也会因为中间层字典不存在而报 KeyError或者因为你用了{}之外的默认值而触发其它类型错误。改用setdefaultdata {} data.setdefault(user, {})[name] 张三这样写如果data里没有user键就自动创建一个空字典作为它的值然后在这个新字典上设置name。嵌套层级多的时候还可以组合使用data.setdefault(user, {}).setdefault(profile, {})[name] 李四这种写法的好处是直截了当不用每次手动判空。但要注意setdefault的返回值是原来那个对象如果没有就是刚创建的默认对象所以对返回值继续赋值是安全的。坏处是如果嵌套特别深超过三层可读性会变差这时候更推荐用普通字典加判空。4.3 类型注解与Optional显式声明在 Python 3.6 中使用类型注解能让“可能为 None”这个语义在代码里显式可见对减少这类报错非常有帮助。from typing import Optional, Dict def get_config() - Optional[Dict[str, str]]: # ... return None # 显式说明可能返回 None config get_config() if config is not None: config[timeout] 30显式标注Optional后配合 mypy 或 PyCharm 的类型检查IDE 会在你写config[timeout] ...时给出警告你可能在解引用一个可能为 None 的值。这里我要多说一句类型注解不仅对编译器有意义更是给未来读代码的人看的文档。我在项目里大量使用类型标注之后团队里类似的问题显著减少因为新的开发者一眼就能看出哪些返回值可能为空哪些不可能为空。4.4 用异常处理兜底但别滥用try-except确实能兜住这个报错try: config[timeout] 30 except TypeError: config {} config[timeout] 30但我强烈不建议把它当作常规用法。理由很简单这个报错是程序逻辑错误的标志如果用 try-except 硬兜住等于把问题盖住了真实原因仍然存在。后续同样的问题还会在其他地方冒出来而且因为少了报错提示排查起来更困难。我见过最头疼的代码就是外层包了一个巨大的try-except Exception把所有异常全吞掉结果出 bug 的时候日志里什么都看不到。正确的姿势是异常处理只用来兜底“预期内的失败”比如网络请求超时而像 NoneType 赋值这种“编程错误”应该让它直接崩出来或者用断言尽早暴露。4.5 初始化时就明确容器类型很多 None 问题源于默认参数或者全局变量的初始化不当。看这个经典反例def append_item(item, my_listNone): my_list.append(item)当my_list的默认值是 None 时.append()会报AttributeError: NoneType object has no attribute append。正确的做法是def append_item(item, my_listNone): if my_list is None: my_list [] my_list.append(item) return my_list这个模式太常见了以至于 Python 官方文档专门有说明不要在函数定义中直接把可变对象如[]作为默认值因为默认值只会在函数定义时创建一次之后所有调用共享同一个对象。而用None作为默认值、在函数内部再做初始化是官方推荐的写法。如果你发现自己经常需要对某个变量做“先判断是不是 None再决定是否初始化”的操作可以考虑用一个辅助函数封装def ensure_list(data): return data if data is not None else []5. 常见问题排查技巧实录这一节是实操问答。我把平时读者和同事问过最多的几个问题整理成速查表方便你遇到问题时直接翻。5.1 速查表报错导航报错信息常见原因排查方向NoneType object does not support item assignment对 None 做obj[key] value或obj[index] value检查 obj 从哪里来为什么是 NoneNoneType object has no attribute xxx对 None 调用属性/方法检查链式调用中某个中间值的返回NoneType object is not subscriptable对 None 做obj[key]访问同上IndexError: list assignment index out of range空列表/越界赋值检查列表长度这张表的核心逻辑是一旦报错信息中出现NoneType object优先追击“它是怎么变成 None 的”而不是纠结“为什么赋值失败”。5.2 调试技巧一处报错三处检查我在排查这个报错时有一套固定的检查流程总结为“三处检查”检查一源头。报错变量是在哪里被创建的初始值是什么检查二路径。从创建到报错之间这个变量经过了多少次赋值有没有可能在某一次赋值中被覆盖成 None检查三契约。如果你是调用了别人的函数这个函数的返回值约定是什么可能返回 None 吗调用前有没有判空这三个检查对应三个动作回溯、搜索、读函数签名的 docstring。熟练之后大多数情况下两分钟就能定位。5.3 踩坑记录一链式赋值与中间态我曾经写过一个数据清洗脚本要对一个多层嵌套的字典做字段更新。一开始的代码长这样data[user][info][city] 杭州运行时报错NoneType object does not support item assignment我第一反应是data或者data[user]是 None。打印出来之后发现data[user]是 None 的因为接口在用户没有完善资料的时候返回了user: null。修复的时候我没有只是加一个判空因为发现代码里类似的地方还有七八处。我改成了用deep_get工具函数统一处理嵌套结构def deep_get(data, path, defaultNone): for key in path: if not isinstance(data, dict) or key not in data: return default data data[key] return data def deep_set(data, path, value): obj data for key in path[:-1]: if key not in obj or not isinstance(obj[key], dict): obj[key] {} obj obj[key] obj[path[-1]] value然后把所有链式赋值都替换成deep_set(data, [user, info, city], 杭州)。虽然多封装了一层但后面接口再传 null 过来脚本也不会崩了而是正常跳过或者创建默认结构。这个工具函数我后来在很多项目里都用到了算是处理嵌套数据的通用方案。5.4 踩坑记录二线程/异步回调中的 None另一个让我印象深刻的案例是异步场景下出现的这个报错。代码逻辑是result None def callback(data): global result result data call_api(callbackcallback) # 假设这里等待回调完成 result[status] done如果回调在result[status] done之前还没有执行那result还是 None这一行就直接触发报错。问题出在“等待回调完成”这个环节——当时的代码没有任何机制保证回调已经执行完毕。修复方式是使用concurrent.futures.Future或者Event来同步from threading import Event done_event Event() def callback(data): global result result data done_event.set() call_api(callbackcallback) done_event.wait(timeout10) # 等待最多 10 秒 if result is not None: result[status] done else: logging.error(callback timeout, result is None)这里的关键不仅是解决类型问题更是在于把异步时序问题显式化保证后续操作的依赖前提成立。这类问题在并发场景下属于高危 bug问题本身不复杂但容易让人误判为普通 None 问题。5.5 踩坑记录三ORM 查询返回 None还有一次是在用 SQLAlchemy 查询时遇到的。查询语句本身没问题但如果数据库里没有匹配记录session.execute(...).scalar_one_or_none()返回的就是 None。当时的代码没有做判空直接对结果赋值线上环境报了这个错。排查时刚开始一直盯着数据表字段后来才发现是业务逻辑问题本应该保证“记录存在”的场景因为某个配置开关关闭后导致记录根本没有写入后续处理里又没有对“无记录”做分支处理。修复方案分两层第一层在查询后立刻判空如果为空就按业务约定处理这里是打日志然后跳过第二层在数据写入流程里加上前置校验确保主流程一定会先创建记录。这类问题的启示是None 不仅仅是一个编程层面的值它还往往是业务状态的映射。当某个对象“不应该是 None 但却是 None”时十有八九是前置业务逻辑没有满足条件。排查时要跳出代码本身回头审视业务流程。6. 扩展思考跨语言的同类问题之所以值得单独聊一点扩展内容是因为这个报错并不是 Python 的专利。在前端领域有一个极其类似的问题就是热词里提到的Uncaught TypeError: Cannot read properties of undefined (reading xxx)。JavaScript 里访问一个未定义对象的属性时会报Cannot read properties of undefined。比如const data undefined; data.name 张三; // TypeError: Cannot set properties of undefinedJavaScript 里的undefined对应 Python 里的None两者都是“空值”的具象化。虽然报错文案、类型系统不同但背后的思维模式完全一致代码假设对象存在且可写但对象实际是空值。处理方式也有共通之处// 前端里常见的防御写法 const user response.data?.user; if (user) { user.name 张三; }这里的可选链运算符?.就是 JavaScript 版的“在访问前判空”。Python 里没有?.这种语法Python 3.12 之前更是如此所以更依赖显式if判断。理解这个跨语言对照有两个实际好处一是面试时能体现出自己对“空值处理”这一编程基础概念的理解深度二是当你需要从 Python 转 JavaScript 或者反过来时能快速迁移已有的排错思路。我个人做技术分享时经常用这个对照来说明**一个编程概念的成熟度不在于你背了多少语法糖而在于你能否在多个语言、多种场景中识别出它背后的同一性问题。**None 与 undefined 的赋值/访问报错就是这样一个经典的同一性问题。处理的所有底层逻辑无非三件事搞清楚变量当前的类型、追溯它从哪来、在访问前做必要的判空和兜底。这三点做到位无论你写 Python、JavaScript 还是别的语言都能玩得转。我到现在写代码的时候仍然会时不时撞上这个报错——但每次撞上它我已经不会像刚开始那样手足无措了。它更像是一个老朋友在提醒我嘿前面有个变量可能是空的你想想该怎么办吧。这个心态转变大概就是经验的价值所在。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SSM图书借阅管理系统:从选题到部署的完整实战指南 2026/9/30 17:35:13

SSM图书借阅管理系统:从选题到部署的完整实战指南

图书借阅管理系统,配上SSM框架,再附上完整源码,这三个词组合起来基本就是Java Web方向毕业设计排行榜上最稳的组合。先说结论:这个题目看起来不大,但覆盖的知识点足够全面,读者管理、图书管理、借书还书、逾…

阅读更多 →
从人形机器人灵巧手看柔性触觉阵列布线设计 2026/9/30 17:35:13

从人形机器人灵巧手看柔性触觉阵列布线设计

人形机器人灵巧手是当前柔性触觉阵列最典型的应用场景之一。它把传感器的工程问题集中放大了:电极密度高、信号通道多、空间受限、动态范围大,而且装配到指节曲面后还要应对弯折疲劳。在这类应用里,电极布线和抗串扰设计直接决定了触觉系统能不能真正发挥价值。从硬件结构上看,…

阅读更多 →
Kerberoast 域内服务账号攻击实战 2026/9/30 17:35:13

Kerberoast 域内服务账号攻击实战

Kerberoast 域内服务账号攻击实战 前言 在域渗透的整个攻击链中,Kerberoast(俗称烤羊毛)是非常经典、杀伤力极强的 AD 攻击技术。它只需要一个普通域用户账号,不需要域管权限、不需要漏洞,只要域内存在注册了 SPN&…

阅读更多 →
大模型变现野路子:五大实战路径与保姆级教程 2026/9/30 17:35:13

大模型变现野路子:五大实战路径与保姆级教程

闷声发大财!用大模型月入5万的5个野路子(附保姆级教程) 先说明一下,我写这篇文章不是来贩卖焦虑的,也不是来跟你保证"照做就能马上月入5万"的。我在大模型应用落地这块摸爬滚打了一年多,接过企业…

阅读更多 →
鸿蒙购物App开发实战:ArkTS+ArkUI全链路落地指南 2026/9/30 17:35:13

鸿蒙购物App开发实战:ArkTS+ArkUI全链路落地指南

1. 项目概述:为什么一个叫“BuyBuyBuy”的鸿蒙购物App,值得花两周时间从零搭起?“江鸟中原”不是地名,也不是人名——它是这个项目的代号,取自“江”南、“鸟”瞰、“中”原三重意象,暗喻应用要覆盖多端、具…

阅读更多 →
分享一下我2026年收藏的17个UI灵感网站 2026/9/30 17:34:59

分享一下我2026年收藏的17个UI灵感网站

最近看到有人聊做 iOS App:初版也不能只求功能能跑,UI 仍要做得精致一点。这样或许更有机会得到设计类推荐。苹果在 App Store 编辑推荐的说明里,也把 UI 设计列为考量之一,编辑还会看用户体验、创新等因素。 做网站也是一样。AI…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉