How should a python type hint require that a value has a given attribute?

一笑奈何 提交于 2020-12-25 01:25:25

问题


Let's say I have a simple function like this:

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

I would like to change the type hint from Any to one that requires (well, suggests, given that it is a type hint) that a provides the bar and baz attributes. What should it be changed to?


回答1:


This is exactly what Protocols are for. In short, Protocols let you use structural instead of nominal subtyping. With nominal subtyping, type A is a subtype of B if A explicitly inherits or extends B. With structural subtyping, type A is a subtype of B if it has the same method and attribute "signatures" as B (with some restrictions).

For example:

# 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"))

You can find more information about using Protocols in the mypy docs. The information in that page is all compliant with the PEP, so the info there should all apply to other type checkers, assuming they've finished implementing their own support for Protocols.

You can also find slightly more complex examples of using Protocols in typeshed, the repository of type hints for the Python standard library.

Though, I suppose this all matters only if you actually intend on using static analysis in your code. If not, you could maybe do something simpler and just define a custom type alias to Any, document what that alias is "supposed" to mean, and use that alias instead of a full-fledged protocol. That alias would be almost completely useless for the purposes of static analysis/autocompletion tools/etc, but humans generally have no issues reading comments.




回答2:


Type hints can only refer to a class, so create an abstract class

import abc

class MyType(abc.ABC):

    @abc.abstractproperty
    def foo(self):
        pass

    @abc.abstractproperty
    def bar(self):
        pass

And declare f(a: MyType)




回答3:


You need to create a class with those attributes so the object you pass has them in it. I.e.:

class Myclass():
    def __init__(self, bar, baz):
        self.bar = bar
        self.baz = baz


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


来源:https://stackoverflow.com/questions/57972163/how-should-a-python-type-hint-require-that-a-value-has-a-given-attribute

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!