静态覆盖函数的参数并保留正确的类型提示和文档字符串

问题描述 投票:0回答:1

我有一个函数

original
,带有指定的参数、它们的类型和文档字符串。

我如何定义一个函数

new
,它基本上会调用
original
,但用一些
a
值覆盖
A_DEFAULT
参数。例如。
new(x) == original(A_DEFAULT, x)

最重要的部分是由此产生的

new

  • 应该有原始签名,但没有
    a
    参数
  • 保留原始文档字符串
  • 签名和文档字符串都应该从 IDE 中可见

目标是在不显式复制原始文档字符串和签名的情况下制作此类部分函数。

有什么想法可以做到吗? (即使你有一些古怪的想法)

import functools


A_DEFAULT  = ...


def original(a: int, b: str):
    "description"
    return a + b


# `new` should have in IDE tooltips and type checks:
# -  __doc__ = "description"
# - signature = (b: int)
# new(x) = original(A_DEFAULT, x)
new = ...

python python-typing
1个回答
0
投票

静态打字

以下解决方案基于此示例此处。对于包括文档字符串在内的静态类型行为,

Concatenate
ParamSpec
证明是有用的:

from typing import Callable, Concatenate, ParamSpec, TypeVar

def original(a: int, b: str):
    "description"
    return str(a) + b

P = ParamSpec("P")
R = TypeVar("R")
T = TypeVar("T")
def set_first_arg_default(
    f: Callable[Concatenate[T, P], R], 
    first_arg_default: T
) -> Callable[P, R]:
    def inner(*args: P.args, **kwargs: P.kwargs) -> R:
        return f(first_arg_default, *args, **kwargs)
    return inner

A_DEFAULT = 1
new = set_first_arg_default(original, A_DEFAULT)
print(new("x"))

静态类型和运行时

如果您还需要在运行时更新文档字符串和签名,可以使用

inspect
库来修改
__signature__
。我将这个示例分开,因为它使解决方案更加复杂:

from typing import Callable, Concatenate, ParamSpec, TypeVar
import inspect

def original(a: int, b: str):
    "description"
    return str(a) + b


P = ParamSpec("P")
R = TypeVar("R")
T = TypeVar("T")
def set_first_arg_default(
    f: Callable[Concatenate[T, P], R], 
    first_arg_default: T
) -> Callable[P, R]:
    def get_new_signature(
        f: Callable[Concatenate[T, P], R]
    ) -> inspect.Signature:
        old_sig =  inspect.signature(f)
        params = list(dict(old_sig.parameters).values())
        return old_sig.replace(parameters=params[1:])

    def inner(*args: P.args, **kwargs: P.kwargs) -> R:
        return f(first_arg_default, *args, **kwargs)
    
    # This section is optional as far as IDEs are concerned
    inner.__doc__ = f.__doc__
    inner.__signature__ = get_new_signature(f) # type: ignore reportFunctionMemberAccess
    return inner

A_DEFAULT = 1
new = set_first_arg_default(original, A_DEFAULT)
print(new("x"))

help(new)
© www.soinside.com 2019 - 2024. All rights reserved.