Python3类型系统:在参数中调用特定属性?

问题描述

假设我要为一组对象创建一个函数,这些对象来自不同的库,但具有相同的流程属性

def get_foo(fooable,fooer):
  return fooer.foo(fooable)

这在任何OOP语言中都是一项有用的功能,它允许您定义对象必须支持的特定接口...

在python的类型提示系统中,如果fooer没有属性foo,我是否可以声明该函数将失败?

# in psuedo code
def get_foo(fooable,fooer: Type.has_attr('foo')):
   return fooer.foo(fooable)

解决方法

Python在collections.abc中原生定义了类似于接口的功能。这是在by using ABC metaclasses,specifically defining the __instancecheck__() and/or __subclasscheck__() methods on a metaclass中实现的。您可以执行类似的操作:定义一个类似于接口的抽象基元类:

>>> class FooableMeta(abc.ABCMeta):
...     def __instancecheck__(self,instance):
...         return (
...             hasattr(instance,'foo')            # has a .foo
...             and callable(instance.foo)          # .foo is callable
...             and not isinstance(instance,type)  # is an instance,not a class
...         )
... 
>>> class Fooer(metaclass=FooableMeta):
...     def __init__():
...         raise NotImplementedError()
...     def foo():
...         raise NotImplementedError()
... 
>>> class Foo:
...     def foo():
...         print("foo")
... 
>>> isinstance(Foo(),Fooable)
True
>>> class Bar:
...     def bar():
...         print("bar")
... 
>>> isinstance(Bar(),Fooable)
False

根据相同的原理,您现在可以使用Fooable进行类型提示。