首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 函数返回值别漏标!显式声明 None 的 3 大好处

Python 函数返回值别漏标!显式声明 None 的 3 大好处

原创
作者头像
小白的大数据之旅
发布2025-09-19 10:28:10
发布2025-09-19 10:28:10
6310
举报

Python 函数返回值别漏标!显式声明 None 的 3 大好处

很多刚写 Python 的朋友,甚至有些老开发者,都会犯一个 “不起眼” 的错:函数明明没有返回值(比如只做打印、保存数据这类操作),却不明确标注返回None。比如写个打印问候的函数:

代码语言:python
复制
# 常见的“偷懒”写法:没标返回类型,也没写return

def print_greeting(name):

   print(f"Hello, {name}!")

你可能觉得 “反正没返回值,标不标无所谓”,但实际用的时候,别人(甚至一周后的你自己)看到这个函数,可能会猜:“它到底返不返回东西?返回的是字符串还是别的?” 万一有人傻呵呵地写result = print_greeting("Bob"),以为result能拿到点什么,最后发现是None,就得花时间查问题 —— 这就是没显式声明返回None的坑。

今天咱们就掰扯清楚:给函数显式声明返回None(也就是写-> None),到底有啥用?学会这一点,能让你的代码更规范、更少 bug,还能在面试时加分!

一、先搞懂:Python 函数的 “隐式返回 None”

在讲好处之前,得先明确一个基础知识点:Python 里所有函数都有返回值。如果函数里没写return语句,或者只写了return(没带值),Python 会自动让它返回None

比如这 3 个函数,实际返回值都是None,效果完全一样:

代码语言:python
复制
# 1. 没写return

def func1():

   print("func1")

# 2. 写了return但没值

def func2():

   print("func2")

   return

# 3. 显式return None

def func3():

   print("func3")

   return None

# 测试一下:三个函数返回值都是None

print(func1())  # 输出 func1 + None

print(func2())  # 输出 func2 + None

print(func3())  # 输出 func3 + None

既然 Python 会 “偷偷” 返回None,为啥还要多此一举显式声明-> None?这就像你出门时,明明门会自动关,但还是会手动推一下确认 —— 为了 “明确” 和 “不出错”。

二、显式声明 None 的 3 大核心好处

接下来逐个讲好处,每个好处都配代码示例,保证你能跟着跑、能理解。

好处 1:静态类型检查 “帮你抓错”,提前避免 bug

现在写 Python 代码,很多团队都会用 “静态类型检查工具”(比如mypy),它能在你运行代码前,就检查出 “返回值类型不匹配” 的错误。而显式声明-> None,就是给这个工具 “指路”,让它知道你这个函数就该返回None,一旦返回了别的东西,马上报错。

举个实际例子:

先安装mypy(用 pip 就行,代码能直接运行):

代码语言:python
复制
pip install mypy

然后写两个函数,一个显式标了-> None,一个没标,看看mypy的反应:

代码语言:python
复制
# 函数1:显式声明返回None,但不小心返回了int(故意写的错代码)

def add_num(a: int, b: int) -> None:

   return a + b  # 这里错了:应该没返回值,却返回了int

# 函数2:没标返回类型,同样返回了int

def multiply_num(a: int, b: int):

   return a * b

mypy检查这个文件(假设文件名叫test.py):

代码语言:python
复制
mypy test.py

你会看到这样的结果:

代码语言:python
复制
test.py:2: error: Incompatible return value type (got "int", expected "None")

Found 1 error in 1 file (checked 1 source file)

——mypy只报错了 “显式标了-> None” 的函数 1,没报错函数 2!因为函数 2 没标返回类型,mypy不知道你想返回啥,就没法检查。

如果函数实际返回None,却标了别的类型,mypy也会报错:

代码语言:python
复制
# 函数3:实际返回None,却标了返回int(错代码)

def print_sum(a: int, b: int) -> int:

   print(a + b)  # 没return,实际返回None

# 用mypy检查,会报:

# test.py:6: error: Missing return statement for function declared to return "int"

总结:显式声明-> None,能让静态检查工具帮你提前发现 “返回值类型不对” 的 bug,不用等代码运行起来才踩坑 —— 尤其是大型项目,这个好处太重要了!

好处 2:代码 “自文档化”,不用读内部逻辑就懂功能

“自文档化” 就是说:别人看你的函数声明,不用读函数里面的代码,就知道这个函数是干嘛的、有没有返回值。显式-> None就像一个 “标签”,告诉所有人:“这个函数只做事,不返回东西,你别指望从它那拿到结果!”

对比一下:显式 vs 隐式的可读性差异

比如有两个处理数据的函数,一个标了-> None,一个没标:

代码语言:python
复制
# 函数A:显式标了-> None

def save_to_db(data: dict) -> None:

   """把数据保存到数据库"""

   # 里面可能有连接数据库、插入数据的代码(省略)

   pass

# 函数B:没标返回类型

def process_data(data: dict):

   """处理数据"""

   # 里面可能有清洗数据、过滤无效值的代码(省略)

   pass

别人用这两个函数时:

  • 看函数 A:-> None + 注释 “保存到数据库”,马上知道 “这是写操作,不返回数据,调用就行,不用赋值”;
  • 看函数 B:没标返回类型,注释只说 “处理数据”—— 到底是 “处理后返回新数据”,还是 “原地处理数据不返回”?必须打开函数内部看代码才知道!
用表格更直观:显式声明 vs 隐式声明的区别

对比维度

显式声明返回 None(def func () -> None)

隐式返回 None(def func ())

可读性

高:一眼知道 “无返回值”

低:需读函数内部才知道

理解成本

低:不用猜功能定位

高:容易误解 “可能有返回值”

维护效率

高:改代码时不会误加返回值

低:改代码时可能忽略返回逻辑

新人友好度

高:新手一看就懂

低:新手容易踩 “赋值无用” 的坑

举个真实场景:比如print()函数,它的官方类型声明就是def print(*args, **kwargs) -> None—— 你一看就知道,调用print("hello")就行,不用写result = print("hello"),因为result肯定是None,没用。

好处 3:区分 “原地操作” 和 “返回新对象”,避免新手高频坑

Python 里有很多 “原地操作” 的函数(比如list.sort()dict.update()),它们会直接修改原对象,不返回新东西(也就是返回None);而对应的 “返回新对象” 函数(比如sorted()dict.copy()),会生成新对象返回,不修改原对象。

很多新手会搞混这俩,比如:

代码语言:python
复制
# 新手常见错误:把list.sort()的返回值赋值给变量

lst = [3, 1, 2]

new_lst = lst.sort()  # 以为new_lst是排序后的列表

print(new_lst)  # 输出None,傻了!

如果显式声明返回None,这个坑就能避免 —— 因为list.sort()的官方类型声明是def sort(...) -> None,一看就知道 “这是原地排序,别赋值!”;而sorted()的声明是def sorted(...) -> list,一看就知道 “返回新列表,要赋值用!”

代码演示:正确区分两者
代码语言:python
复制
# 1. 原地操作函数:list.sort() -> None

lst1 = [3, 1, 2]

print("排序前lst1:", lst1)  # 输出:排序前lst1: [3, 1, 2]

# 调用sort(),不赋值(因为返回None)

lst1.sort()

print("排序后lst1:", lst1)  # 输出:排序后lst1: [1, 2, 3]

# 2. 返回新对象函数:sorted() -> list

lst2 = [3, 1, 2]

print("排序前lst2:", lst2)  # 输出:排序前lst2: [3, 1, 2]

# 调用sorted(),赋值(因为返回新列表)

new_lst2 = sorted(lst2)

print("新列表new_lst2:", new_lst2)  # 输出:新列表new_lst2: [1, 2, 3]

print("原列表lst2没变:", lst2)      # 输出:原列表lst2没变: [3, 1, 2]

# 3. 再举一个:dict.update() -> None(原地修改) vs dict.copy() -> dict(返回新对象)

d1 = {"name": "Alice"}

# update()是原地修改,返回None,不赋值

d1.update({"age": 20})

print("修改后d1:", d1)  # 输出:修改后d1: {'name': 'Alice', 'age': 20}

d2 = {"name": "Bob"}

# copy()返回新字典,要赋值

new_d2 = d2.copy()

new_d2["age"] = 22

print("原d2:", d2)      # 输出:原d2: {'name': 'Bob'}

print("新new_d2:", new_d2)  # 输出:新new_d2: {'name': 'Bob', 'age': 22}

总结:显式-> None就像 “警告牌”,告诉使用者 “这个函数只改原对象,别赋值!”,完美避开 “赋值 None” 的新手坑。

三、常见错误:这些 “返回 None” 的坑你踩过吗?

讲完好处,再聊聊大家常犯的错误,每个错误都给代码示例,帮你避坑。

错误 1:隐式返回 None,却标注了其他类型

比如函数实际没返回值(返回 None),但类型注解标了-> strmypy会报错,自己也容易误解:

代码语言:python
复制
# 错代码:实际返回None,却标了-> str

def get_user_name(user_id: int) -> str:

   print(f"用户ID: {user_id}")  # 没return,实际返回None

# 用mypy检查,报错:

# Missing return statement for function declared to return "str"

正确写法:如果函数只打印不返回,就标-> None

代码语言:python
复制
def get_user_name(user_id: int) -> None:

   print(f"用户ID: {user_id}")

错误 2:显式返回 None,但代码里多此一举

有些人为了 “显式”,会在函数末尾加return None,但其实如果函数里已经有return(没值),或者函数逻辑到最后自然结束,再加return None就是多余的:

代码语言:python
复制
# 没必要的写法:函数逻辑到最后结束,加return None多余

def print_age(age: int) -> None:

   if age < 18:

       print("未成年")

       return  # 这里已经返回None了

   print("成年")

   return None  # 多余!删掉不影响

# 正确简化写法:

def print_age(age: int) -> None:

   if age < 18:

       print("未成年")

       return

   print("成年")

注意:不是不能写return None,而是 “没必要”—— 显式标注-> None已经够了,函数末尾的return None可省则省,代码更简洁。

错误 3:误用返回 None 的函数赋值

这是新手最高频的错,比如用list.sort()dict.update()的返回值赋值:

代码语言:python
复制
# 错1:用list.sort()赋值

lst = [5, 2, 4]

sorted_lst = lst.sort()  # sorted_lst是None

print(sorted_lst[0])  # 报错:TypeError: 'NoneType' object is not subscriptable

# 错2:用dict.update()赋值

d = {"a": 1}

new_d = d.update({"b": 2})  # new_d是None

print(new_d["b"])  # 同样报错

正确做法:记住 “标了-> None的函数,调用时别赋值”,直接用:

代码语言:python
复制
lst = [5, 2, 4]

lst.sort()  # 直接调用,不赋值

print(lst[0])  # 输出2,正确

d = {"a": 1}

d.update({"b": 2})  # 直接调用,不赋值

print(d["b"])  # 输出2,正确

四、面试高频题:关于 “返回 None” 的问题怎么答?

面试时,面试官可能会问这些问题,结合咱们讲的知识点,用大白话答就行,不用背术语。

问题 1:为什么要给 Python 函数显式声明返回 None?

回答:主要有 3 个原因:

  1. 能让静态类型检查工具(比如 mypy)帮着抓错,比如函数不小心返回了别的类型,工具会提前报错;
  2. 代码更易读,别人一看-> None就知道函数 “只做事不返回值”,不用读内部代码;
  3. 能区分 “原地操作” 和 “返回新对象”,比如list.sort() -> None,一看就知道别赋值,避免踩坑。

问题 2:list.sort () 和 sorted () 的返回值有什么区别?显式声明返回 None 在这里有什么用?

回答

  • list.sort()是原地排序,返回 None(显式标了-> None),调用时不能赋值,赋值会得到 None;
  • sorted()是返回新的排序后的列表(标了-> list),调用时要赋值,才拿得到新列表;
  • 显式声明-> None的作用是 “提醒使用者”:list.sort()只改原列表,别指望从它那拿返回值,避免新手赋值用的错误。

问题 3:如果函数实际返回 None,但类型注解写了 -> int,会有什么问题?怎么解决?

回答

  • 问题:静态类型检查工具(比如 mypy)会报错,提示 “返回值类型不匹配”;而且别人用的时候会误以为函数返回 int,赋值后发现是 None,导致 bug;
  • 解决:把类型注解改成-> None,明确告诉工具和使用者,函数返回 None。

问题 4:写一个函数,实现 “原地去重列表” 的功能,并显式声明返回 None,说明为什么这么设计?

回答

先写函数:

代码语言:python
复制
def remove_duplicates(lst: list) -> None:

   """原地去除列表中的重复元素(保持第一次出现的顺序)"""

   # 用临时集合存已出现的元素,遍历列表修改

   seen = set()

   i = 0

   while i < len(lst):

       if lst[i] in seen:

           lst.pop(i)  # 原地删除重复元素

       else:

           seen.add(lst[i])

           i += 1

# 测试

my_lst = [1, 2, 2, 3, 1]

remove_duplicates(my_lst)

print(my_lst)  # 输出:[1, 2, 3]

设计原因:

  1. 函数是 “原地操作”(直接修改原列表),不返回新列表,所以显式标-> None,告诉使用者别赋值;
  2. 显式标注后,静态检查工具能帮着检查 —— 如果不小心在函数里加了return lst,工具会报错,避免返回值类型不对;
  3. 别人看声明就知道函数是 “原地去重”,不用读内部代码,理解成本低。

五、总结:小细节,大作用

显式声明函数返回None,看起来是个小细节,但能解决很多实际问题:

  • 提前抓 bug(静态检查);
  • 让代码更易读(自文档);
  • 避开新手坑(区分原地操作)。

尤其在团队开发或写开源代码时,这个小细节能显著降低沟通成本和维护成本 —— 别人不用猜你的函数行为,你自己后期改代码也不会迷糊。

从现在开始,写 Python 函数时:

  • 如果函数没返回值(只做操作),就加上-> None
  • 看到标了-> None的函数,别赋值用;
  • mypy这类工具辅助检查,让代码更规范。

这么做,你的代码会越来越专业,bug 也会越来越少!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Python 函数返回值别漏标!显式声明 None 的 3 大好处
    • 一、先搞懂:Python 函数的 “隐式返回 None”
    • 二、显式声明 None 的 3 大核心好处
      • 好处 1:静态类型检查 “帮你抓错”,提前避免 bug
      • 好处 2:代码 “自文档化”,不用读内部逻辑就懂功能
      • 好处 3:区分 “原地操作” 和 “返回新对象”,避免新手高频坑
    • 三、常见错误:这些 “返回 None” 的坑你踩过吗?
      • 错误 1:隐式返回 None,却标注了其他类型
      • 错误 2:显式返回 None,但代码里多此一举
      • 错误 3:误用返回 None 的函数赋值
    • 四、面试高频题:关于 “返回 None” 的问题怎么答?
      • 问题 1:为什么要给 Python 函数显式声明返回 None?
      • 问题 2:list.sort () 和 sorted () 的返回值有什么区别?显式声明返回 None 在这里有什么用?
      • 问题 3:如果函数实际返回 None,但类型注解写了 -> int,会有什么问题?怎么解决?
      • 问题 4:写一个函数,实现 “原地去重列表” 的功能,并显式声明返回 None,说明为什么这么设计?
    • 五、总结:小细节,大作用
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档