在Python中,如何使用具有与包含功能相同的签名的方法进行装饰班?

发布于 2025-01-31 09:48:02 字数 3084 浏览 3 评论 0原文

这是我要做的事情的示例:

from typing import Callable, Generic, ParamSpec, TypeVar

t_Params = ParamSpec("t_Params")
t_Ret = TypeVar("t_Ret")
class Wraps(Generic[t_Params, t_Ret]):

    _func: Callable[t_Params, t_Ret]

    def __call__(self, func: Callable[t_Params, t_Ret]):
        self._func = func
        return self

    def call(self, *args: t_Params.args, **kwargs: t_Params.kwargs) -> t_Ret:
        return self._func(*args, **kwargs)

@Wraps()
def rep(a: str, b: int, **kwargs):
    return a * b

我不想使用__ INIT __设置self._func,因为我将无法将其他参数传递到__ INIT __

我希望__调用__方法可以推断t_paramst_ret的类型,但这不是:

# mousing over `rep` and `rep.func` shows:

rep      # (function) rep: Wraps[(*args: Unknown, **kwargs: Unknown), Unknown]
rep.func # (method) call: (...) -> Unknown

我相信这是因为类型是因为类型AREN' t在创建类实例时被推断出来,而是在调用实例时。

我希望wraps.call方法具有与包装函数相同的签名。有什么办法做到这一点吗?


编辑以进行进度更新:

如果我远离__调用__是定义类实例的通用类型的内容,我将制作一个替代构造函数,将其所有参数供应到__ INT __ INT __ INT __ INT __ func参数之后。但是,我无法给出替代构造函数(.new)正确的类型提示。

from typing import Callable, Generic, TypeVar

t_WrapsCallable = TypeVar("t_WrapsCallable", contravariant=True, bound=Callable)
t_Callable = TypeVar("t_Callable", bound=Callable)

class Wraps(Generic[t_WrapsCallable]):

    func: t_WrapsCallable

    def __init__(self, func: t_WrapsCallable):
        self.func = func
        return self

    @classmethod
    def new(cls, *args, **kwargs) -> Callable[[t_Callable], "Wraps[t_Callable]"]:
        def _new(func: t_Callable) -> Wraps[t_Callable]:
            return cls(func, *args, **kwargs)
        return _new

@Wraps.new()
def rep(a: str, b: int):
    return a * b

rep      # (function) rep: Wraps[(a: str, b: int) -> str]
rep.func # (variable) func: (a: str, b: int) -> str

class WrapsWithName(Wraps[t_WrapsCallable]):

    def __init__(self, func: t_WrapsCallable, name: str):
        super().__init__(func)
        self.name = name

    @classmethod
    def new(cls, name: str) -> Callable[[t_Callable], "WrapsWithName[t_Callable]"]:
        # The following line has a type issue:
        # Function return type "Wraps[t_Callable@new]" is incompatible with type "WrapsWithName[t_Callable@new]"
        #   "Wraps[t_Callable@new]" is incompatible with "WrapsWithName[t_Callable@new]"
        return super().new(name)

@WrapsWithName.new("repeat")
def rep2(a: str, b: int):
    return a * b

rep2      # (function) rep2: WrapsWithName[(a: str, b: int) -> str]
rep2.func # (variable) func: (a: str, b: int) -> str
rep2.name # (variable) name: str

我不能嵌套generic s,或.new函数将返回callable [[t_callable],self [t_callable]]自我是一种与自我阶级结合的typevar。

这使我提出了一个替代问题:python缺乏对高态度类型的支持有什么办法吗?

Here's an example of what I'm trying to do:

from typing import Callable, Generic, ParamSpec, TypeVar

t_Params = ParamSpec("t_Params")
t_Ret = TypeVar("t_Ret")
class Wraps(Generic[t_Params, t_Ret]):

    _func: Callable[t_Params, t_Ret]

    def __call__(self, func: Callable[t_Params, t_Ret]):
        self._func = func
        return self

    def call(self, *args: t_Params.args, **kwargs: t_Params.kwargs) -> t_Ret:
        return self._func(*args, **kwargs)

@Wraps()
def rep(a: str, b: int, **kwargs):
    return a * b

I don't want to use __init__ to set self._func because I would not be able to pass other arguments into __init__.

I would expect the __call__ method to infer the type of t_Params and t_Ret, but it does not:

# mousing over `rep` and `rep.func` shows:

rep      # (function) rep: Wraps[(*args: Unknown, **kwargs: Unknown), Unknown]
rep.func # (method) call: (...) -> Unknown

I believe this is because the types aren't being inferred when the class instance is created, but rather when the instance is called.

I want the Wraps.call method to have the same signature as the wrapped function. Is there any way to do this?


Editing to update with progress:

If I move away from __call__ being what defines the generic types for the class instance, I make an alternate constructor that feeds all of its arguments to __init__ after the func argument. However, I can't give the alternate constructor (.new) the correct type hint.

from typing import Callable, Generic, TypeVar

t_WrapsCallable = TypeVar("t_WrapsCallable", contravariant=True, bound=Callable)
t_Callable = TypeVar("t_Callable", bound=Callable)

class Wraps(Generic[t_WrapsCallable]):

    func: t_WrapsCallable

    def __init__(self, func: t_WrapsCallable):
        self.func = func
        return self

    @classmethod
    def new(cls, *args, **kwargs) -> Callable[[t_Callable], "Wraps[t_Callable]"]:
        def _new(func: t_Callable) -> Wraps[t_Callable]:
            return cls(func, *args, **kwargs)
        return _new

@Wraps.new()
def rep(a: str, b: int):
    return a * b

rep      # (function) rep: Wraps[(a: str, b: int) -> str]
rep.func # (variable) func: (a: str, b: int) -> str

class WrapsWithName(Wraps[t_WrapsCallable]):

    def __init__(self, func: t_WrapsCallable, name: str):
        super().__init__(func)
        self.name = name

    @classmethod
    def new(cls, name: str) -> Callable[[t_Callable], "WrapsWithName[t_Callable]"]:
        # The following line has a type issue:
        # Function return type "Wraps[t_Callable@new]" is incompatible with type "WrapsWithName[t_Callable@new]"
        #   "Wraps[t_Callable@new]" is incompatible with "WrapsWithName[t_Callable@new]"
        return super().new(name)

@WrapsWithName.new("repeat")
def rep2(a: str, b: int):
    return a * b

rep2      # (function) rep2: WrapsWithName[(a: str, b: int) -> str]
rep2.func # (variable) func: (a: str, b: int) -> str
rep2.name # (variable) name: str

I can't nest Generics, or the .new function would return something like Callable[[t_Callable], Self[t_Callable]] where Self is a TypeVar that binds to the self's class.

This brings me to an alternate question: Is there a way around Python's lack of support for higher-kinded-types?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文