python中的多重分派
plum-dispatch的Python项目详细描述
Plum: Multiple Dispatch in Python
每个人都喜欢多发,就像每个人都喜欢李子一样。
安装
pip install plum-dispatch
基本用法
多分派允许您为同一对象实现多个方法 函数,其中方法指定其参数的类型:
fromplumimportdispatch@dispatch(str)deff(x):return'This is a string!'@dispatch(int)deff(x):return'This is an integer!'
>>>f('1')'This is a string!'>>>f(1)'This is an integer!'
我们还没有为float
s实现方法,因此在这种情况下出现异常
将被提升:
>>>f(1.0)NotFoundLookupError:Forfunction"f",signature(builtins.float)couldnotberesolved.
与其为float
s实现方法,不如为
所有号码:
fromnumbersimportNumber@dispatch(Number)deff(x):return'This is a number!'
由于float
是Number
,因此f(1.0)
将返回'This is a number!'
。
但是int
也是Number
,因此f(1)
可以返回
'This is an integer!'
或'This is a number!'
。
多重分派的规则是选择最具体的方法:
>>>f(1)'This is an integer!'
因为int
是Number
,但是Number
不一定是int
。
有关多个分派的详细概述,请参见 manual of the Julia Language。
按示例列出的功能
从类型注释分派
Dispatcher.annotations
是一个实验特性,可以用来
对函数的类型批注进行调度:
fromplumimportdispatch,add_conversion_methodadd_conversion_method(type_from=int,type_to=str,f=str)@dispatch.annotations()defint_to_str(x:int)->str:returnx@dispatch.annotations()defint_to_str(x):raiseValueError('I only take integers!')
>>>int_to_str(1.0)ValueError:Ionlytakeintegers!>>>int_to_str(1)'1'
接头类型
集合可用于实例化联合类型:
fromplumimportdispatch@dispatch(object)deff(x):print('fallback')@dispatch({int,str})deff(x):print('int or str')
>>> f(1)
int or str
>>> f('1')
int or str
>>> f(1.0)
fallback
参数类型
参数类型Tuple
和List
可用于在元组上分派
以及包含特定类型元素的列表。
重要的是,类型系统是covariant,而不是julia的类型
系统,它是不变的。
fromplumimportdispatch,Tuple,List@dispatch({tuple,list})deff(x):print('tuple or list')@dispatch(Tuple(int))deff(x):print('tuple of int')@dispatch(List(int))deff(x):print('list of int')
>>>f([1,2])'list of int'>>>f([1,'2'])'tuple or list'>>>f((1,2))'tuple of int'>>>f((1,'2'))'tuple or list'
变量参数
列表可用于指定变量参数:
fromplumimportdispatch@dispatch(int)deff(x):print('single argument')@dispatch(int,[int])deff(x,*xs):print('multiple arguments')
>>> f(1)
single argument
>>> f(1, 2)
multiple arguments
>>> f(1, 2, 3)
multiple arguments
返回类型
关键字参数return_type
可以设置为指定返回类型:
fromplumimportdispatch,add_conversion_method@dispatch({int,str},return_type=int)deff(x):returnx
>>>f(1)1>>>f('1')TypeError:Expectedreturntype"builtins.int",butgottype"builtins.str".>>>add_conversion_method(type_from=str,type_to=int,f=int)>>>f('1')1
遗传
因为python中的每个类都可以被子类化,所以对角线分派不能 实施。 但是,继承可以用来实现一种对角分派:
fromplumimportDispatcher,Referentiable,SelfclassReal(Referentiable):dispatch=Dispatcher(in_class=Self)@dispatch(Self)def__add__(self,other):return'real'classRational(Real,Referentiable):dispatch=Dispatcher(in_class=Self)@dispatch(Self)def__add__(self,other):return'rational'real=Real()rational=Rational()
>>> real + real
'real'
>>> real + rational
'real'
>>> rational + real
'real'
>>> rational + rational
'rational'
转换
函数convert
可用于将一种类型的对象转换为另一种类型:
fromnumbersimportNumberfromplumimportconvertclassRational(object):def__init__(self,num,denom):self.num=numself.denom=denom
>>>convert(0.5,Number)0.5>>>convert(Rational(1,2),Number)TypeError:Cannotconverta"__main__.Rational"toa"numbers.Number".
TypeError
表示convert
不知道如何转换
Rational
到Number
。
让我们实现这种转换:
fromoperatorimporttruedivfromplumimportconversion_method@conversion_method(type_from=Rational,type_to=Number)defrational_to_number(q):returntruediv(q.num,q.denom)
>>>convert(Rational(1,2),Number)0.5
除了装饰符conversion_method
,还可以使用
add_conversion_method
:
fromplumimportadd_conversion_methodadd_conversion_method(type_from,type_to,conversion_function)
促销
函数promote
可用于将对象提升为公共类型:
fromplumimportdispatch,promote,add_promotion_rule,add_conversion_method@dispatch(object,object)defadd(x,y):returnadd(*promote(x,y))@dispatch(int,int)defadd(x,y):returnx+y@dispatch(float,float)defadd(x,y):returnx+y
>>>add(1,2)3>>>add(1.0,2.0)3.0>>>add(1,2.0)TypeError:Nopromotionrulefor"builtins.int"and"builtins.float".>>>add_promotion_rule(int,float,float)>>>add(1,2.0)TypeError:Cannotconverta"builtins.int"toa"builtins.float".>>>add_conversion_method(type_from=int,type_to=float,f=float)>>>add(1,2.0)3.0
方法优先级
关键字参数precedence
可以设置为整数值以指定
用于打破歧义的方法的优先级:
fromplumimportdispatchclassElement(object):passclassZeroElement(Element):passclassSpecialisedElement(Element):pass@dispatch(ZeroElement,Element)defmul_no_precedence(a,b):return'zero'@dispatch(Element,SpecialisedElement)defmul_no_precedence(a,b):return'specialised operation'@dispatch(ZeroElement,Element,precedence=1)defmul(a,b):return'zero'@dispatch(Element,SpecialisedElement)defmul(a,b):return'specialised operation'
>>>zero=ZeroElement()>>>specialised_element=SpecialisedElement()>>>element=Element()>>>mul(zero,element)'zero'>>>mul(element,specialised_element)'specialised operation'>>>mul_no_precedence(zero,specialised_element)AmbiguousLookupError:Forfunction"mul_no_precedence",signature(__main__.ZeroElement,__main__.SpecialisedElement)isambiguousamongthefollowing:(__main__.ZeroElement,__main__.Element)(precedence:0)(__main__.Element,__main__.SpecialisedElement)(precedence:0)>>>mul(zero,specialised_element)'zero'
参数类
decoratorparametric
可用于创建参数类:
fromplumimportdispatch,parametric@parametricclassA(object):# Must be a new-style class!pass@dispatch(A)deff(x):return'fallback'@dispatch(A(1))deff(x):return'1'@dispatch(A(2))deff(x):return'2'
>>>A__main__.A>>>A(1)__main__.A{1}>>>issubclass(A(1),A)True>>>A(1)()<__main__.A{1}at0x10c2bab70>>>>f(A(1)())'1'>>>f(A(2)())'2'>>>f(A(3)())'fallback'
添加多种方法
Dispatcher.multi
可用于同时实现多个方法:
fromplumimportdispatch@dispatch.multi((int,int),(float,float))defadd(x,y):returnx+y
>>>add(1,1)2>>>add(1.0,1.0)2.0>>>add(1,1.0)NotFoundLookupError:Forfunction"add",signature(builtins.int,builtins.float)couldnotberesolved.
从另一个包扩展函数
Function.extend
可用于扩展特定函数:
frompackageimportf@f.extend(int)deff(x):return'new behaviour'
>>>f(1.0)'old behaviour'>>>f(1)'new behaviour'
直接调用方法
Function.invoke
可用于调用给定类型参数的方法:
fromplumimportdispatch@dispatch(int)deff(x):return'int'@dispatch(str)deff(x):return'str'
>>>f(1)'int'>>>f('1')'str'>>>f.invoke(int)('1')'int'>>>f.invoke(str)(1)'str'