Python类型提示应该如何要求一个值具有给定的属性?

16

假设我有一个这样的简单函数:

def foo(a: Any):
    return a.bar + a.baz

我想将类型提示从Any更改为建议(考虑到它是一种类型提示)需要a提供barbaz属性。 应该把它改成什么?


你能否只使用类类型,例如 MyClass,该类将具有这些属性? - vahvero
3
但这将限制foo只能接受MyClass实例,而它以前并没有受到限制。 - Rob Gilton
3个回答

27
这正是 协议 的用处。简而言之,协议使你可以使用 结构化 而非 名义上的 子类型检测。名义上的子类型检测中,如果类型A明确继承或扩展了类型B,那么类型A就是类型B的子类型。在结构化子类型检测中,如果类型A的方法和属性“签名”与类型B相同(有一些限制),则类型A就是类型B的子类型。
例如:
# If you're using Python 3.8+
from typing import Protocol

# If you need to support older versions of Python,
# pip-install the 'typing_extensions' module and do:
from typing_extensions import Protocol

class SupportsBarBaz(Protocol):
    bar: int
    baz: int

class MyUnrelatedClass1:
    def __init__(self, bar: int, baz: int) -> None:
        self.bar = bar
        self.baz = baz

class MyUnrelatedClass2:
    def __init__(self, bar: int, baz: int, blah: str) -> None:
        self.bar = bar
        self.baz = baz
        self.blah = blah

class MyUnrelatedClass3:
    def __init__(self, bar: str, baz: str, blah: str) -> None:
        self.bar = bar
        self.baz = baz
        self.blah = blah

def foo(a: SupportsBarBaz) -> int:
    return a.bar + a.baz

# These both type-check, even though there's no explicit relationship
# between 'SupportsBarBaz' and these two classes
foo(MyUnrelatedClass1(1, 2))
foo(MyUnrelatedClass2(1, 2, "abc"))

# But this doesn't type-check, since 'bar' and 'baz' are both strs here
foo(MyUnrelatedClass3("a", "b", "c"))

你可以在mypy文档中找到更多使用协议的信息。该页面上的信息都符合PEP规范,因此假设其他类型检查器已完成对协议的支持,则该页面上的信息应适用于其他类型检查器。

您还可以在typeshed中的类型提示存储库中找到更复杂的使用协议的示例。

不过,我想这一切只有当您实际上打算在您的代码中使用静态分析时才有意义。如果没有,您可能可以简单地定义自定义类型别名为Any,记录该别名的“预期”含义,并使用该别名代替完整的协议。对于静态分析/自动完成工具等目的,该别名几乎完全无用,但人们通常没有阅读注释的问题。


1

类型提示只能引用类,因此创建一个抽象类

import abc

class MyType(abc.ABC):

    @abc.abstractproperty
    def foo(self):
        pass

    @abc.abstractproperty
    def bar(self):
        pass

并声明 f(a: MyType)



1
这是否意味着传递到 foo 中的任何值都需要继承自 MyType - Rob Gilton
这是动态语言中的一个灰色地带。它是一种“提示”类型,实际上并没有做任何事情。然而,Python标准库使用了这种方法。请参见https://docs.python.org/3/library/collections.abc.html。 - blue_note
把它看作是“a实现了接口MyType”,而不是“a属于类MyType”。一个对象可以实现多个接口。 - blue_note
好的 - 谢谢。这正是我所期望的方法,但我发现通常不清楚是否这是“正确”的做法。请注意,@abstractproperty现在已在标准库中被弃用。 - Rob Gilton

-1
你需要创建一个类,包含这些属性,以便传递的对象具有这些属性。例如:
class Myclass():
    def __init__(self, bar, baz):
        self.bar = bar
        self.baz = baz


def foo(a: Myclass):
    return a.bar + a.baz

这将限制foo始终采用Myclass实例。可能有一大组潜在无关的类具有barbaz属性。 - Rob Gilton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接