繁体   English   中英

如何指定可调用 python 数据类成员的类型以接受子类实例

[英]How to specify the type of a callable python dataclass member to accept subclass instances

我希望能够指定一个可调用的数据 class 成员,并采用派生 class 的实例。如果我使用抽象成员函数,这将很容易。

我尝试过如下代码,但不知道为 Callable 类型规范提供什么参数。 我正在寻找一种方法来注释handler属性,以便派生类可以将自己的实例作为handler (在本例中为Specific )的参数,而不仅仅是Base

@dc.dataclass
class Base():
    name: str
    handler: typing.Callable[['Base'], str] # <--- What to use instead of 'Base'


@dc.dataclass
class Specific(Base):
    specific: str


# This is the callable that I would like to use.
def specific_handler(v: Specific) -> str:
    return f"{v.specific}"

# Assigning specific_handler to handler gives a type error.
sg = Specific(name="two", handler=specific_handler, specific="extra info")

# The handler can be used in following manner.
assert("extra info" == sg.handler(sg))

我正在使用 Python 3.7。

解决方案仍然涉及typing.TypeVar

只要handler的第一个参数不接受与拥有的 class 相同类型的参数,那么Base就必须是通用的(如果它相同类型,您可以使用typing.Self来逃避)。 handler的第一个参数是否是Base的子类并不重要,这只是您添加到typing.TypeVar(bound=...)的细节。

import dataclasses as dc
import typing

T = typing.TypeVar("T", bound="Base[typing.Any]")

@dc.dataclass
class Base(typing.Generic[T]):
    name: str
    handler: typing.Callable[[T], str]

# `Base["Specific"]` assumes that you want `Specific.handler` to be of type `typing.Callable[[Specific], str]`, which is true in this situation.
# Otherwise just provide another subclass of `Base` (or `typing.Any`).
@dc.dataclass
class Specific(Base["Specific"]):
    specific: str

def specific_handler(v: Specific) -> str:
    return f"{v.specific}"

sg = Specific(name="two", handler=specific_handler, specific="extra info")
assert "extra info" == sg.handler(sg)

虽然@dROOOze 的回答有效,但没有必要冗长(需要重复 class 名称作为通用属性)。 下面的替代解决方案使用typing.Self (通过typing_extensions在 python 3.11 上添加,通过typing_extensions向后移植,在mypy master 上受支持 - 尽管 0.991 仍然缺少此功能)。 所以,如果你可以使用 master mypy分支或者稍后阅读这个答案,那么mypy 1.0及以上版本完全支持Self

这是一个游乐场

import sys
from dataclasses import dataclass
from typing import Callable
# You can omit this guard for specific target version
if sys.version_info < (3, 11):
    from typing_extensions import Self
else:
    from typing import Self


@dataclass
class Base:
    name: str
    handler: Callable[[Self], str]

@dataclass
class Specific(Base):
    specific: str

def specific_handler(v: Specific) -> str:
    return f"{v.specific}"


sg = Specific(name="two", handler=specific_handler, specific="extra info")
assert "extra info" == sg.handler(sg)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM