You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实例方法默认返回值包装器:类内装饰器实现及优化问询

问题描述

我有十几个实例方法,这些方法仅在满足特定条件时执行核心逻辑,否则返回各自不同的默认值,初始实现如下:

def fun(self, *args, **kwargs):
    if not self.init():
        return ... # <- 默认值,每个方法都不同
    # 方法核心逻辑
    return ... # 业务返回值

我通过装饰器实现了该逻辑,代码如下:

from functools import partial, wraps


def check_init(caller = None, *, ret = None):
    if caller is None:
        return partial(check_init, ret = ret)
    
    @wraps(caller)
    def _check_init(self, *args, **kwargs):
        return caller(self, *args, **kwargs) if self.init() else ret
    return _check_init


class Test:
    num: int = 2

    def init(self) -> bool:
        return self.num < 5

    @check_init(ret = 'Cannot show number')
    def show(self) -> str:
        return 'Number is %d' % self.num


x = Test()

print(x.show()) # "Number is 2"

x.num = 4
print(x.show()) # "Number is 4"

x.num = 6
print(x.show()) # "Cannot show number"

现在有两个技术问题:

  • 由于该逻辑仅与Test类的实例方法相关,如何将完整的check_init装饰器代码块放入Test类内部?放在类外时无法为self添加Test或Self类型提示。
  • 是否存在更符合Python风格(Pythonic)的实现方式?最好仍使用方法装饰器。
解决方案

1. 将装饰器放入类内部并完善类型提示

可以把装饰器定义为类方法嵌入Test类中,结合Self类型(从typing导入)标注self参数,既能让装饰器归属于类内部逻辑,又能解决类型提示缺失的问题。

示例代码:

from functools import partial, wraps
from typing import Self, Callable, Any


class Test:
    num: int = 2

    @classmethod
    def check_init(cls, caller: Callable[..., Any] | None = None, *, ret: Any = None) -> Callable:
        if caller is None:
            return partial(cls.check_init, ret=ret)
        
        @wraps(caller)
        def _check_init(self: Self, *args: Any, **kwargs: Any) -> Any:
            return caller(self, *args, **kwargs) if self.init() else ret
        return _check_init

    def init(self) -> bool:
        return self.num < 5

    @check_init(ret='Cannot show number')
    def show(self) -> str:
        return 'Number is %d' % self.num


x = Test()
print(x.show())  # Number is 2
x.num = 4
print(x.show())  # Number is 4
x.num = 6
print(x.show())  # Cannot show number

2. 更Pythonic的装饰器实现

基于方法装饰器的前提下,可通过类型参数化和清晰的嵌套结构优化代码,让类型提示更精准、可读性更强:

from functools import wraps
from typing import Self, Callable, ParamSpec, TypeVar

P = ParamSpec('P')
R = TypeVar('R')


class Test:
    num: int = 2

    @classmethod
    def check_init(cls, default_ret: Any) -> Callable[[Callable[P, R]], Callable[P, R | Any]]:
        def decorator(func: Callable[P, R]) -> Callable[P, R | Any]:
            @wraps(func)
            def wrapper(self: Self, *args: P.args, **kwargs: P.kwargs) -> R | Any:
                return func(self, *args, **kwargs) if self.init() else default_ret
            return wrapper
        return decorator

    def init(self) -> bool:
        return self.num < 5

    @check_init(default_ret='Cannot show number')
    def show(self) -> str:
        return 'Number is %d' % self.num

这个版本用ParamSpec和TypeVar保留原方法的参数与返回值类型,类型检查更严谨;同时拆分装饰器嵌套结构,逻辑层级更清晰,符合Python的可读性优先原则。


内容的提问来源于stack exchange,提问作者Marko Gulin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:03:16