Python 类型暗示与duck类型
在Python中使用类型暗示的缺点之一是牺牲Python代码的美 在类型提示之前,我的方法签名是简洁的:Python 类型暗示与duck类型,python,type-hinting,Python,Type Hinting,在Python中使用类型暗示的缺点之一是牺牲Python代码的美 在类型提示之前,我的方法签名是简洁的: def echo(items): for i in items: print(i) 由于我的团队正在使用类型提示,我也在代码中添加了类型提示: def echo(items: Set[str]) -> None: from typing_extensions import Protocol, runtime_checkable @runtime_
def echo(items):
for i in items:
print(i)
由于我的团队正在使用类型提示,我也在代码中添加了类型提示:
def echo(items: Set[str]) -> None:
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable
class Portable(Protocol):
handles: int
class Mug:
def __init__(self) -> None:
self.handles = 1
mug = Mug()
if isinstance(mug, Portable):
use(mug.handles) # Works statically and at runtime
还是挺容易走路的。一段时间后,我的代码中对集合集进行操作的其他部分要求我的项可以散列,而其他部分则不需要。所以我决定也支持frozenset
,现在我的方法如下:
def echo(items: Union[Set[str],Frozenset[str]]) -> None:
它开始看起来像Java中的方法,尽管在Java中我可以在接口上操作,而忽略了实现细节:
void echo(Set<String> items) {
void echo(设置项){
Python不支持接口概念,也就是说,我不能说Set
实现了Frozenset
。由于duck类型,最初的实现将同时适用于Set
和Frozenset
:两者都表现为Set。然而,我的印象是,显式类型暗示在某种程度上并不适合duck类型平
如何在键入提示和duck键入之间找到一个良好的平衡点?使用AbstractSet
:
from typing import AbstractSet
def echo(items: AbstractSet[str]) -> None:
...
AbstractSet
|
|
+--------------+
| |
| |
MutableSet FrozenSet
|
|
Set
Set
和FrozenSet
都从AbstractSet
继承(直接或间接):
from typing import AbstractSet
def echo(items: AbstractSet[str]) -> None:
...
AbstractSet
|
|
+--------------+
| |
| |
MutableSet FrozenSet
|
|
Set
正如@chpner所说的,使用内置类型是一个很好的选择
协议的概念与接口有一些相似之处
如文件所述:
结构子类型可以看作是Python程序员熟知的duck类型的静态等价物。Mypy通过下面描述的协议类提供对结构子类型的支持。有关Python中协议和结构子类型的详细规范,请参阅
还可以定义自定义协议:
from typing import Iterable
from typing_extensions import Protocol
class SupportsClose(Protocol):
def close(self) -> None:
... # Empty method body (explicit '...')
class Resource: # No SupportsClose base class!
# ... some methods ...
def close(self) -> None:
self.resource.release()
def close_all(items: Iterable[SupportsClose]) -> None:
for item in items:
item.close()
close_all([Resource(), open('some/file')]) # Okay!
虽然协议的主要用途是静态分析,但它们允许检查对象在运行时是否也遵循某些协议:
def echo(items: Set[str]) -> None:
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable
class Portable(Protocol):
handles: int
class Mug:
def __init__(self) -> None:
self.handles = 1
mug = Mug()
if isinstance(mug, Portable):
use(mug.handles) # Works statically and at runtime
我会让其他人详细说明,但至少要解决接口问题。duck类型并不是一个真正的问题,duck类型通过Protocol
s支持。这只是一个使用适当的公共祖先而不是枚举类的问题。你能提供一个到Python代码库的链接,在这里可以解释这种关系吗icitly implemented?Java JDK包含类似于公共类HashSet implements Set
的内容。查看类型。Frozenset
我只能看到Frozenset=\u别名(Frozenset,T\u co,inst=False)
。它是从中明确记录的父类中收集的。因此协议看起来有点像Rust中的traits。我需要更多地了解它。谢谢。traits实际上不是一个新概念。但它们有点不同。Go在方法上使用结构类型来确定类型与接口的兼容性。“协议的概念与接口有一些相似之处。”——不仅仅是相似性。它们是相同的。协议是OO中的一个基本概念。记住,面向对象的基础是消息传递,网络隐喻无处不在。(Alan Kay认识那些在发明Smalltalk的同时发明互联网的人,并且对他们的工作非常感兴趣。object==网络主机,method==隐藏的本地代码,field==隐藏的本地存储,message==公共API的想法在Smalltalk的设计中非常明显。)一开始,Smalltalk的想法协议"虽然是非正式的,但很快就正式集成到SimultAutoDes中。然后研究了第一个SMTALTE类型系统,这些都是基于网络规范和网络协议的思想。当Brad Cox和Tom Love把SimultTalk对象模型放到C上并创建Objto-C时,他们提出了协议PAR的思想。Objective-C是Java设计的主要影响因素,Java中的interface
关键字直接来自Objective-C协议。因此,我们可以从Smalltalk中的协议到Java中的接口画一条直线。