假设我有一个带有签名的函数:
def foo(self, name:str, stream):
pass
我想在" stream"中添加注释。参数,这意味着"你可以拥有任何对象x只要x.readline() - > str。
这意味着我可以在这里使用任何python文件对象作为参数(因为它有一个readline方法),但是我还可以提供一个只实现readline的对象,它完全可以接受。
我怎样才能重写这个函数定义,以便我可以注释第二个参数?
答案 0 :(得分:6)
PEP 544 https://www.python.org/dev/peps/pep-0544/提出了结构子类型(静态鸭子类型)。如果/当它被接受时你将不需要一个明确的子类,你将能够简单地定义自己的协议,静态类型检查器将理解这些协议。
答案 1 :(得分:3)
此解决方案并不等同于您正在寻找的内容:
只要
,您就可以拥有任何对象xx.readline() -> str
相反,我们定义了一个自定义抽象基类,它希望readline
抽象方法由其子类定义。因此,它不会接受任何随机对象,而只接受这个新抽象基类的实例,使其更加明确。
from abc import ABC, abstractmethod
class FileObject(ABC):
@abstractmethod
def readline(self):
raise NotImplementedError()
现在我们要定义一个可以使用Python的文件对象和FileObject
实例的自定义类型:
from typing import IO, TypeVar
StreamType = TypeVar('StreamType', IO, FileObject)
def func(name: str, stream: StreamType) -> None:
pass
现在让我们使用mypy测试它:
from io import StringIO, BytesIO
class X(FileObject):
def readline(self):
pass
func('a', StringIO()) # passed
func('a', BytesIO()) # passed
func('a', open('foo.txt')) # passed
func('a', X()) # passed
func('a', object()) # failed
func('a', []) # failed
func('a', 1) # failed
<强>输出:强>
$ mypy so.py
so.py:33: error: Type argument 1 of "func" has incompatible value "object"
so.py:34: error: Type argument 1 of "func" has incompatible value List[None]
so.py:35: error: Type argument 1 of "func" has incompatible value "int"
答案 2 :(得分:0)
正如ivanl所指出的,the online version here添加了协议来支持“静态鸭子类型”。该PEP最近已被接受,并将成为Python 3.8的一部分。您已经可以使用PEP 544软件包在Mypy上尝试使用Protocols(在Python 3.6+中)。
在您的情况下,您将使用单个方法定义一个非常简单的协议SupportsReadline
,并在函数参数的注释中使用它:
# In 3.8 this can become 'from typing ...'
from typing_extensions import Protocol
class SupportsReadline(Protocol):
def readline(self) -> str:
...
def func(name: str, stream: SupportsReadline) -> None:
pass
现在,具有readline
方法且具有兼容签名的任何对象都是SupportsReadline
的隐式子类型,并且满足函数参数的注释。请注意,LineRepeater
不会显式继承自SupportsReadline
:
class LineRepeater:
def readline(self) -> str:
return "Hello again!"
func("a", LineRepeater()) # OK
如果方法签名完全匹配
,则其他对象也是如此:from io import BytesIO, StringIO
func("a", StringIO()) # OK
func("a", open("foo.txt")) # OK
func("a", BytesIO()) # ERROR (return type is bytes instead of str)
func("a", []) # ERROR
func("a", 1) # ERROR
func("a", object()) # ERROR