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

前端 未结 3 1207
长情又很酷
长情又很酷 2021-01-03 09:31

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

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

I would lik

3条回答
  •  生来不讨喜
    2021-01-03 09:49

    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.

提交回复
热议问题