如何定义自定义的无特殊形式类型

发布于 2025-02-13 13:24:08 字数 629 浏览 0 评论 0原文

我需要以下的行为:

NoOp = ...  # don't know how to define this

def foo(bar: NoOp[int]) -> int:
    return bar + 1

foo(1)

我希望Mypy像bar完全一样,就像我只是将其注释为bar:int。我还需要它与int本身不同,以便编写代码,以检查是否用Eg noop [str] vs Just str noop >。只是Mypy应该对待他们。


我试图与 newtype得到类似的东西,但是由于它将结果定义视为一个子类,因此无法用于我的目的,而我必须使用它的方式也创建了非常非常的代码很难阅读。

I need something that behaves as follows:

NoOp = ...  # don't know how to define this

def foo(bar: NoOp[int]) -> int:
    return bar + 1

foo(1)

I want mypy to treat bar exactly as if I had simply annotated it as bar: int. I also need it to be distinct from int itself in order to write code that checks if something was annotated with e.g. NoOp[str] vs just str. It's just mypy that should treat them the same.


I tried to work with NewType to get something similar, but since it treats the resulting definition as a subclass it didn't work for my purposes, and the way I had to use it also created code that was very hard to read.

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

方圜几里 2025-02-20 13:24:08

如果我正确理解,noop [t]t在运行时和类型检查器的眼中都是完全相同的类型。唯一的区别是您要编写检查注释的自定义代码,并且能够告诉tnoop [t]外。这是正确的吗?

如果是这样,则可以查看 注释 类型,在Python 3.9中引入,并通过typing_extensions进行了回波。它采用注释的[t,x]的形式,其中t是一种类型注释,x是任意的元数据。它允许您将元数据x与注释t相关联,而不更改类型(IE 注释[T,x]等于<代码> t )。

对于您的情况,您可以将noop定义为通用类型别名:

T = TypeVar("T")
NoOp = Annotated[T, "no-op"]  # use "no-op" as metadata, but you could just choose any value

def foo(bar: NoOp[int]) -> int:
    return bar + 1

然后,您可以使用 typing.get_type_hints (替换typingtyping_extensions在3.9之前的版本)。注释对象将具有__元数据__属性,该属性容纳了元数据的元组:

>>> typing.get_type_hints(foo)  # by default it strips the metadata
{'x': int, 'return': int}

>>> typing.get_type_hints(foo, include_extras=True)
{'x': typing.Annotated[int, 'no-op'], 'return': int}

>>> x_annotation = typing.get_type_hints(foo, include_extras=True)["x"]
>>> x_annotation.__metadata__
('no-op',)

原因是元组,因为您可以将多个值与之相关联,例如注释[INT,“ A” ,“ B”,1.234]

If I understand correctly, NoOp[T] and T are exactly the same type both at runtime and also in the eyes of the type checker. The only difference is that you want to write custom code that inspects the annotations, and be able to tell T apart from NoOp[T]. Is this correct?

If so, then you could check out the Annotated type, introduced in Python 3.9 and backported via typing_extensions. It takes the form of Annotated[T, X], where T is a type annotation and X is arbitrary metadata. It allows you to associate the metadata X with annotation T, while not changing the type (i.e. Annotated[T, X] is equivalent to T).

For your case, you could define NoOp as a generic type alias:

T = TypeVar("T")
NoOp = Annotated[T, "no-op"]  # use "no-op" as metadata, but you could just choose any value

def foo(bar: NoOp[int]) -> int:
    return bar + 1

You can then inspect the annotations with typing.get_type_hints (replace typing with typing_extensions for versions prior to 3.9). The annotation object will have a __metadata__ attribute, which holds a tuple of the metadata:

>>> typing.get_type_hints(foo)  # by default it strips the metadata
{'x': int, 'return': int}

>>> typing.get_type_hints(foo, include_extras=True)
{'x': typing.Annotated[int, 'no-op'], 'return': int}

>>> x_annotation = typing.get_type_hints(foo, include_extras=True)["x"]
>>> x_annotation.__metadata__
('no-op',)

The reason it's a tuple is because you could associate multiple values with it, e.g. Annotated[int, "a", "b", 1.234].

掀纱窥君容 2025-02-20 13:24:08

有些疑问,因为我不确定我会得到您的用户酶 - 对不起,如果这不是您的需求。

from typing import Generic, TypeVar

T = TypeVar('T')   # you can better specify this if needed

class Wrapper(Generic[T]):
    def __init__(content: T):
    self.content = content


def f(x: Wrapper[int] | int) -> int:
    if isinstance(x, Wrapper): 
        return x.content + 1
    return x + 1

另一方面,如果您想制作包装器以便在f中进行实例检查,则可以做类似的

    def __add__(self, other: Wrapper[T] | T):
        if isinstance(other, Wrapper):
            return Wrapper(self.content + other.content)
        return Wrapper(self.content + other)

操作需要小心,因为您最终会出现一个非交易__添加__(不一定是错误的,但要注意的是什么)。

With some doubts because I am not sure I got your usecase -- sorry if this is not what you need.

from typing import Generic, TypeVar

T = TypeVar('T')   # you can better specify this if needed

class Wrapper(Generic[T]):
    def __init__(content: T):
    self.content = content


def f(x: Wrapper[int] | int) -> int:
    if isinstance(x, Wrapper): 
        return x.content + 1
    return x + 1

If on the other hand you want to make Wrapper to behave functorially so that you don't need to do instance checks within f, you could do something like

    def __add__(self, other: Wrapper[T] | T):
        if isinstance(other, Wrapper):
            return Wrapper(self.content + other.content)
        return Wrapper(self.content + other)

but you'd need to be careful because you'll end up with a non-commutative __add__ (not necessarily wrong, but something to be aware of).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文