如何将自定义协议与 Callable 协议结合起来? [英] How to combine a custom protocol with the Callable protocol?

查看:28
本文介绍了如何将自定义协议与 Callable 协议结合起来?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个装饰器,它接受一个函数并返回具有一些附加属性的相同函数:

I have a decorator that takes a function and returns the same function with some added attributes:

import functools
from typing import *


def decorator(func: Callable) -> Callable:
    func.attr1 = "spam"
    func.attr2 = "eggs"
    return func

如何键入提示decorator 的返回值?我希望类型提示传达两条信息:

How do I type hint the return value of decorator? I want the type hint to convey two pieces of information:

  1. 返回值是一个Callable
  2. 返回值具有属性attr1attr2

如果我写一个协议,

class CallableWithAttrs(Protocol):
    attr1: str
    attr2: str

然后我失去了Callable.显然我不能让协议继承自 Callable;

then I lose Callable. And apparently I can't make the protocol inherit from Callable;

class CallableWithAttrs(Callable, Protocol):
    attr1: str
    attr2: str

mypy 说:

error: Invalid base class "Callable"

另一方面,如果我只使用Callable,我会丢失有关添加属性的信息.

On the other hand, if I just use Callable, I lose the information about the added attributes.

这在引入类型变量时可能更加复杂,即当装饰器必须返回与给定函数 func 相同类型的可调用对象时,正如 MisterMiyagi 在评论中指出的那样.

This is perhaps even more complicated when introducing type variables, i.e. when the decorator must return the same type of callable as the given function func, as pointed out by MisterMiyagi in the comments.

import functools
from typing import *

C = TypeVar('C', bound=Callable)


def decorator(func: C) -> C:
    func.attr1 = "spam"
    func.attr2 = "eggs"
    return func

现在我该怎么办?我不能从类型变量继承:

Now what do I do? I can't inherit from a type variable:

class CallableWithAttrs(C, Protocol):
    attr1: str
    attr2: str

error: Invalid base class "C"

推荐答案

可以通过Callable参数化一个Protocol:

from typing import Callable, TypeVar, Protocol

C = TypeVar('C', bound=Callable)  # placeholder for any Callable


class CallableObj(Protocol[C]):   # Protocol is parameterised by Callable C ...
    attr1: str
    attr2: str

    __call__: C                   # ... which defines the signature of the protocol

这会创建Protocol 本身与任意Callable 的交集.

This creates an intersection of the Protocol itself with an arbitrary Callable.

接受任何可调用 C 的函数因此可以返回 CallableObj[C],一个具有所需属性的相同签名的可调用对象:

A function that takes any callable C can thus return CallableObj[C], a callable of the same signature with the desired attributes:

def decorator(func: C) -> CallableObj[C]: ...

MyPy 正确识别签名和属性:

MyPy properly recognizes both the signature and attributes:

def dummy(arg: str) -> int: ...

reveal_type(decorator(dummy))           # CallableObj[def (arg: builtins.str) -> builtins.int]'
reveal_type(decorator(dummy)('Hello'))  # int
reveal_type(decorator(dummy).attr1)     # str
decorator(dummy)(b'Fail')  # error: Argument 1 to "dummy" has incompatible type "bytes"; expected "str"
decorator(dummy).attr3     # error: "CallableObj[Callable[[str], int]]" has no attribute "attr3"; maybe "attr2"?

这篇关于如何将自定义协议与 Callable 协议结合起来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆