Python中如何优雅地解决业务代码中重复嵌套检查返回结果是否为None的问题?
在改进学校学长学姐的一些项目,感觉代码结构组织的一团糟,现在又不好做大的修改。
他们的代码一个明显的问题就是很多函数或者方法的调用,如果相关结果不对,就直接返回 None(或者 False)。这样的后果是,等程序真正出了问题的时候,除了一步步 debug我很难找到这个 None 到底是哪个地方返回的。
我现在的想法是重新修改代码,在有可能返回 None 的地方,我自己重新检查一遍返回结果是否为 None ,如果返回结果是 None 我自己另外再做处理。但是这样感觉写出的代码太丑陋了,代码里面充满if xxx is None式检查语句。
请问 V 站各位前辈对于上面这种情况我应该如何优雅处理?
Python中如何优雅地解决业务代码中重复嵌套检查返回结果是否为None的问题?
这是异常处理?
如果出异常了,需要中断处理,那么就抛异常。
如果出异常了,可以继续执行,那么就忽略异常,一般就返回 None 了。
# 使用装饰器统一处理None检查
def none_check(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if result is None:
raise ValueError(f"函数 {func.__name__} 返回了None值")
return result
return wrapper
# 使用链式调用处理多层嵌套
class ResultWrapper:
def __init__(self, value):
self.value = value
def bind(self, func):
if self.value is None:
return ResultWrapper(None)
try:
return ResultWrapper(func(self.value))
except Exception:
return ResultWrapper(None)
def get_or_default(self, default):
return self.value if self.value is not None else default
# 使用Optional类型和模式匹配(Python 3.10+)
from typing import Optional
def process_data(data: Optional[dict]) -> Optional[str]:
match data:
case None:
return None
case {"user": {"profile": {"name": name}}} if name:
return name.upper()
case _:
return None
# 使用Maybe模式简化代码
class Maybe:
def __init__(self, value):
self.value = value
def map(self, func):
return Maybe(func(self.value)) if self.value is not None else Maybe(None)
def get_or_else(self, default):
return self.value if self.value is not None else default
# 实际使用示例
@none_check
def get_user_data(user_id):
# 模拟可能返回None的函数
return {"name": "Alice"} if user_id == 1 else None
# 链式调用示例
result = (ResultWrapper(1)
.bind(lambda x: x + 1)
.bind(lambda x: x * 2)
.get_or_default(0))
# Maybe模式示例
maybe_result = Maybe(5).map(lambda x: x * 2).get_or_else(0)
装饰器适合统一处理函数返回值的None检查,链式调用能优雅处理多层操作,Maybe模式提供了函数式编程的解决方案,模式匹配则让代码更清晰。根据场景选最合适的方案就行。
关键步骤记日志?
他们的代码的本意应该就是出了异常,认为可以忽略异常,然后就返回 None 。但是这样的做法假设有一个函数或者方法,它被嵌套调用了多次,所以在一系列嵌套调用的过程必须每一次都要检查 None ,要不然等出了错就不知道异常到底是哪一步骤抛出来的。
我现在有两种做法:
1. 原本返回 None 的地方我不兼容异常,全部抛出
2. 在返回 None 的地方做检查,并且记录日志,但是这样的话代码结构会很丑
请问上面做法是否合理?
这个问题,我觉得可以等同于:请问 V 站各位前辈不得不吃屎时是如何优雅处理的?
其中的煎熬难以言喻,其路漫漫,其修远兮~
把鼻子堵上,
先为现有的业务逻辑建立一些测试用例,然后再开始修改现有实现,保证前面的测试用例通过,逐步修改
把需要用到的代码封装一下再用
装饰器不就是在这时候用的吗。
1 可以把一些逻辑拆出来。然后写单元测试~
2 抛出异常,捕获异常写日志~~
try catch 就是这个时候用的啊。我一般是喜欢 在关键部分: try: except KeyError xxx except IndexError…
Python 不熟,但 script 类语言应该都可以写一个调用函数的函数解决:_call_func 有 3 个参数,函数名,参数个数,参数对象数组(或集合什么的能放下所有种类对象的)。 用这个函数调用所有的其他函数,在这个函数里面检查返回值,如果是 none ,就抛出异常,或者打印函数名称。 对象方法也可以,重载_call_func 第一个参数是对象,第二个参数是对象的方法,其他一样。然后把所有直接调用都改成调用函数调用。
用日志打印出来吧,出问题 返回 None 之前,先把 当前的函数名打印出来,再返回。

