Package: pyoverload
pyoverloadIntroductionpyoverloadis a package affiliated to projectPyCAMIA. It is a powerful overloading tools to provide easy overload forpython v3.6+.pyoverloadprovide multiple usages. The simplest one, however, can be easily implemented as follows.>>>frompyoverloadimportoverload>>>@overload...deffunc(x:int):...print("func1",x)...>>>@overload...deffunc(x:str):...print("func2",x)...>>>func(1)func11>>>func("1")func21pyoverloadhas all of following appealing features:Support ofJediauto-completionby keyword decorator@overload. This means all main-stream python IDE can hint you the overloaded functions you have defined.Multiple usagesthat are user friendly for all kinds of users, includingC/Javalanguage system users and those who are used tosingledispatchbased overload. Also, easy collector of ordinary python functions is also provided.Support ofall kinds of functions, including functions, methods, class methods and static methods. One simple implementation for all.String typessupported. This means that one can use"numpy.ndarray"to mark a numpy array without importing the whole package.Sufficientbuilt-in types are providedfor easy representations such asList[Int],Dict@{str: int}orList<<int>>[10].Available usage listingwhen no overload function matches the input arguments.Type constraint for an ordinary functionusing@paramsdecorator.InstallationThis package can be installed bypip install pyoverloador moving the source code to the directory of python libraries (the source code can be downloaded ongithuborPyPI).pipinstallpyoverloadUsagesUsage 1: Decorator FashionOne can use@overloadbefore the functions with the same function name to build an overloaded function. When the function is called, the inputs will be handed out to the suitable implementation.The types of the input arguments are specified by the typehints available inpython3.6+. All known types can be added after the colon. For package classes likenp.ndarray, please use a string to represent it. For more types, one can use types from packagetypesorpyoverload.typehint.For usage ofpyoverload.typehint, please refer to sectionTypehintsfor more information.All implementations of the overloaded function are referenced in the order of definition, but the implementation ends with__default__or__0__will be used when no usage is available. Note that there arefourunderlines for this notation,twoon each side.>>>frompyoverloadimport*>>>importnumpyasnp>>>@overload...deffunc__default__(x):...print("func1",x)...>>>@overload...deffunc(x:int):...print("func2",x)...>>>@overload...deffunc(x:str):...print("func3",x)...>>>@overload...deffunc(x:List<<Int>>[4]):...print("func4",x)...>>>@overload...deffunc(x:'np.ndarray'):...print("func5",x)...>>>func(1)func21>>>func("1")func31>>>func([1,2,3,4])func4[1,2,3,4]>>>func(np.array([1,2,3,4]))func5[1234]>>>func(1.)func11.0Note that the auto-completion byJedican only work for this usage.Usage 2: Registering FashionAfter using@overloaddecorator, apart from using@overloadto decorate functions with the same name, one can also use the decorator with the function name@{fill in the function name}to decorate other functions with relevant names likefunc1,func_str,first_funcfor functionfunc. However, omitting sign_is recommended for these functions.In this fashion, the default function is the one decorated with@overloadthough it can still be changed by adding__default__or__0__tags in the decorated function names. All typehints are the same as the first usage.The following example realized the first three functions in the usage 1 example in a reimplementation.>>>frompyoverloadimportoverload>>>@overload...deffunc(x):...print("func1",x)...>>>@func...deffunc2(x:int):...print("func2",x)...>>>@func...def_(x:str):...print("func3",x)...Note thatusage 1andusage 2can be used together though you may need to specify the default function manually like in usage 1 if needed. The last example is rewrote in a combined style.>>>frompyoverloadimportoverload>>>@overload...deffunc__default__(x):...print("func1",x)...>>>@overload...deffunc(x:int):...print("func2",x)...>>>@func...def_(x:str):...print("func3",x)...Usage 3: Collector FashionThe last possible usage can not be used along with the previous two, or at least this is not tested by the developer and is not recommended. Another decorator@overrideis used as a collector.In this usage, all functions should have different names and all functions with typehints should add decorator@paramsto activate the typehint regularization. Collector@overridetakes additional function list as the arguments indicating these functions should be packed into a single function.Note that the last function in the function list is the default function.>>>frompyoverloadimportoverride,params>>>@params...deffunc_all(x):...print("func1",x)...>>>@params...deffunc_int(x:int):...print("func2",x)...>>>@params...deffunc_str(x:str):...print("func3",x)...>>>@override(func_int,func_str,func_all)...deffunc():pass...Theoretically, decorator@overridecan also be used inusages 1&2, but this is not recommended either.Implementation ListWhen an overloaded function receives arguments that are not suitable for all implementations, the error information will tell you which ones are correct.>>>frompyoverloadimportoverload>>>@overload...deffunc(x:int):...print("func1",x)...>>>@overload...deffunc(x:str):...print("func2",x)...>>>func(1.)Traceback(mostrecentcalllast):[...omitted...]NameError:Nofunc()matchesarguments1.0.Allavailableusagesare:func(x:int)func(x:str)This function is available for all two Implementations but none of them takes1..TypehintsDecorator@paramsenables functions to reject inputs with wrong types by raisingTypeHintError. One can use it directly to decorate functions with python typehints or one can add some arguments to it. In the following example, we apply the condition thatais a function,bis an integer,kis a series of integers while functiontest_funcneeds to return an iterable type of real numbers with length2.>>>frompyoverloadimport*>>>@params(Func,Int,+Int,__return__=Real[2])...deftest_func(a,b=2,*k):...print(a,b,k)...returnk...>>>test_func(lambdax:1,3,4,5)<function<lambda>at0x7fbdb2027f70>3(4,5)(4,5)The basic types inpyoverload.typehintareBool,Int,Float,Str,Set,List,Tuple,Dict,Callable,Function,Method,Lambda,Functional,Real,Null,Sequence,Array,Iterable,Scalar,IntScalar,FloatScalar. Among which,callablecontains all callable objects including callable classes whileFuncconsists all kinds of actual functions.Function, however, only refer to explicitly defined functions, andMethodandLambdarefer to the class methods and anonymous functions respectively. These three types are all contained in typeFunctional.Realis apyoverload.typehint.Typewhilereal, a list[int, float], is not.nullis the type of elementNonewhileNullis thepyoverload.typehint.Typeform of it.sequenceis[tuple, list, set], whileSequenceis thepyoverload.typehint.Typeform.Arrayis the type of package based tensors. Only the tensors ofnumpy,torch,tensorflowandbatorchare currently supported. UseArray.Numpy,Array.Torch,Array.TensorflowandArray.Batorchto identify specific packages.IterableincludesArray,Sequenceanddict.Three types ofScalars support the array variables.All these types are subclasses oftypeand instances ofpyoverload.typehint.Typewhich will be abbreviated asTypein the following introduction.One can useType(int)to convert a python type likeintto aTypeor useType(int, float)to combine multiple existing types. Recurrence is also acceptable,Type(Type(int, float), Type(str))is equivalent toType(int, float, str). It will be better if a keywordnameis also assigned.For aType,Listfor example, we can do the following operations. Except the first four, these usages are designed for iterable types only.+List: This indicates that this is an extendable argument, which means it decorates arguments after*. It was used to specify*argsarguments in@paramsbut currently deprecated though adding+would not lead to failures.~List: This invert the typehint, meaning that all non-list types.List|Tuple: This is equivalent toType(List, Tuple)indicating list or tuples.Type(A)&Callable: Theandoperator. Note that both indicator should beTypes.>>>frompyoverloadimport*>>>classA:pass...>>>classB(A):pass...>>>classC(A):...def__call__(self):pass...>>>isoftype(B(),Type(A)&Callable)False>>>isoftype(C(),Type(A)&Callable)TrueList[5]: This indicates the length or shape of the variable. For other types such asInt[5], this indicates an iterable of length 5 with integer elements. Note thatArray[3, 4]represents an array with shape3 x 4, but this representation is not available for nested lists. UseList[List[4]][3]orList<<List[4]>>[3]instead.List@[int, str]: This only works for ordered sequenceListandTuple. It indicates that the list has 2 elements, and the first one is an integer while the second is a string. Note that this isnotequivalent toList[int, str]which represents a list with elements either integer or string.List@intorDict@{str: int}: For all Iterables,A@Bindicates typeAwith all elements of typeB.Dict@{str:int}represents a dictionary with string keys and integer values.List[int]:=List@int. A list with integer elements. Note that one can useList[Int|float]to directly identify a list of elements that are eitherintorfloat.List[int, float]andDict[str: int, int:str]are also valid for multiple element types or key-value pairs. Using list in the blankets will return to the effect of@operator:List[[int, float]]=List@[int, float].List<<int>>[10]:=(List@int)[10]=List[int][10]The first one of the two types should be aType. The representation refers to an integer list of length 10 which is equivalent toList[10]@int. The length or shape is not specified if a pair of empty blankets is given. UseList<<Type(int, Float)>>[]to represent multiple candidates andDict<<(str,int)>>[]=Dict[str:int]to represent a dictionary.len(List[10, 20]): Functionlenreturns the length of the array.200should be the result for the given example.SuggestionsThe speed of type check forpyoverload.typehint.Typeis not very fast, hence please try your best to use builtin types, types from moduletypesor list of types to do the typehint.Theoverloadtakes extra time for delivering the arguments, hence using it for functions require fast speed is not recommended.Do use@paramsfor functions not overloaded but needs typehint constraints instead of using@overloadwithout actually has multiple implementations. This is because@paramsis way faster.Acknowledgment@Yuncheng Zhou: Developer