Python 利用函数注解实现方法重载 问题
你已经学过怎样使用函数参数注解,那么你可能会想利用它来实现基于类型的方法重载。 但是你不确定应该怎样去实现(或者到底行得通不)。
Python 利用函数注解实现方法重载 解决方案
本小节的技术是基于一个简单的技术,那就是Python允许参数注解,代码可以像下面这样写:
下面是我们第一步的尝试,使用到了一个元类和描述器:
为了使用这个类,你可以像下面这样写:
下面是一个交互示例来验证它能正确的工作:
Python 利用函数注解实现方法重载 讨论
坦白来讲,相对于通常的代码而已本节使用到了很多的魔法代码。 但是,它却能让我们深入理解元类和描述器的底层工作原理, 并能加深对这些概念的印象。因此,就算你并不会立即去应用本节的技术, 它的一些底层思想却会影响到其它涉及到元类、描述器和函数注解的编程技术。
本节的实现中的主要思路其实是很简单的。MultipleMeta
元类使用它的 __prepare__()
方法 来提供一个作为 MultiDict
实例的自定义字典。这个跟普通字典不一样的是, MultiDict
会在元素被设置的时候检查是否已经存在,如果存在的话,重复的元素会在 MultiMethod
实例中合并。
MultiMethod
实例通过构建从类型签名到函数的映射来收集方法。 在这个构建过程中,函数注解被用来收集这些签名然后构建这个映射。 这个过程在 MultiMethod.register()
方法中实现。 这种映射的一个关键特点是对于多个方法,所有参数类型都必须要指定,否则就会报错。
为了让 MultiMethod
实例模拟一个调用,它的 __call__()
方法被实现了。 这个方法从所有排除 self
的参数中构建一个类型元组,在内部map中查找这个方法, 然后调用相应的方法。为了能让 MultiMethod
实例在类定义时正确操作,__get__()
是必须得实现的。 它被用来构建正确的绑定方法。比如:
不过本节的实现还有一些限制,其中一个是它不能使用关键字参数。例如:
也许有其他的方法能添加这种支持,但是它需要一个完全不同的方法映射方式。 问题在于关键字参数的出现是没有顺序的。当它跟位置参数混合使用时, 那你的参数就会变得比较混乱了,这时候你不得不在 __call__()
方法中先去做个排序。
同样对于继承也是有限制的,例如,类似下面这种代码就不能正常工作:
原因是因为 x:A
注解不能成功匹配子类实例(比如B的实例),如下:
作为使用元类和注解的一种替代方案,可以通过描述器来实现类似的效果。例如:
为了使用描述器版本,你需要像下面这样写:
描述器方案同样也有前面提到的限制(不支持关键字参数和继承)。
所有事物都是平等的,有好有坏,也许最好的办法就是在普通代码中避免使用方法重载。 不过有些特殊情况下还是有意义的,比如基于模式匹配的方法重载程序中。