问题
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