入职新公司之后一直致力于bug修复以及新功能开发, 所以很久没有写笔记了
使用 python 函数签名自动生成默认返回值
-
先说一下使用场景吧 数据完整性要求不高,但是又不能影响下游
-
一般来说都是
配合retry食用
-
具体使用场景俺也不清楚
, 仁者见仁智者见智吧, 或者魔改一下呢
from typing import Tuple
def run_func():
@retry(max_retries=1, delay=1)
def example_function() -> Tuple[str, str]:
# 假设这里抛出了异常, 但是返回值又是元组, 则会导致下游解包错误
raise TypeError('111')
print(example_function())
# 当然, 你也可以在外层加入判断或者别的方式去做, 但是总觉得少了点什么
-
再说下代码编写的格式吧
-
要遵循手动 类型标注 并且复杂类型要遵循
typing
方式去做 -
可能兼容的类型不全或者有bug
, 但是原理是有了, 增加自己需要兼容的返回值类型就可以了 -
接下来上代码(纯属笔记)
# -*- coding: UTF-8 -*-
# @author: ylw
# @file: demo
# @time: 2024/6/28
# @desc:
# import sys
# import os
# F_PATH = os.path.dirname(__file__)
# sys.path.append(os.path.join(F_PATH, '..'))
# sys.path.append(os.path.join(F_PATH, '../..'))
import time
import typing
import traceback
import inspect
from functools import wraps
DEFAULT_VALUES = {
int: 0,
float: 0.0,
str: '',
list: [],
dict: {},
tuple: (),
set: set(),
bool: False,
None: None
}
def generate_default_value(return_type: typing.Any) -> typing.Any:
"""
根据返回类型生成默认值
生效条件为手动添加了函数签名(返回值类型)
:param return_type:
inspect.signature(func).return_annotation
"""
if return_type in DEFAULT_VALUES:
return DEFAULT_VALUES[return_type]
elif isinstance(return_type, getattr(typing, '_GenericAlias')):
origin = return_type.__origin__
args = typing.get_args(return_type)
if origin is typing.Union:
return generate_default_value(args[0])
elif origin is list:
return [generate_default_value(args[0])] if args else []
elif origin is dict:
return {generate_default_value(args[0]): generate_default_value(args[1])} if len(args) == 2 else {}
elif origin is tuple:
return tuple(generate_default_value(arg) for arg in args)
elif origin is set:
return set(generate_default_value(arg) for arg in args[0]) if args else set()
elif origin is typing.Tuple:
return tuple(DEFAULT_VALUES.get(arg, None) for arg in args)
return None
def get_traceback_str(error: Exception) -> str:
return '\n'.join([i.strip().replace(' ', ' ') for i in traceback.format_tb(error.__traceback__)])
def retry(max_retries=3, delay=0, *, save_error_log: bool = True, no_exception=False):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
error = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if save_error_log:
tb = get_traceback_str(e)
print(f"{tb}\n{attempt + 1}/{max_retries}")
if delay > 0:
time.sleep(delay)
error = e
if no_exception:
sig = inspect.signature(func)
return generate_default_value(sig.return_annotation)
raise error
return wrapper
return decorator
def run_func():
# 示例函数
@retry(1, 1, no_exception=True)
def example_function() -> typing.Tuple[None, str]:
raise TypeError('111')
print(example_function())
if __name__ == '__main__':
run_func()