import inspect class OverloadedError(Exception): pass def GetMethodName(name): digit = name.rstrip('_')[-1] return ''.join(name.rsplit(digit, 1)) class MethodOverloading(type): def __new__(cls, classname, bases, classDict): newDict = {} overloads = {} for attr, item in classDict.items(): if callable(item) and attr.rstrip('_') and attr.rstrip('_')[-1].isdigit(): newName = GetMethodName(attr) if newName in classDict: raise OverloadedError("Method '%s' is also overloaded." % newName) overloadList = overloads.setdefault(newName, []) overloadList.append((attr, item)) else: newDict[attr] = item storedOverloads = {} newDict['_overloads'] = storedOverloads for methodName, overloadList in overloads.items(): thisMethod = {} for entry in overloadList: name, method = entry (args, _, __, defaults) = inspect.getargspec(method) args = args[1:] if len(args) != len(defaults): raise OverloadedError("Overloaded method '%s' has non-keyword arguments." % name) thisSignature = [] for arg, val in zip(args, defaults): if not isinstance(val, type): break thisSignature.append(val) if not thisSignature: raise OverloadedError("Overloaded method '%s' has no types specified." % name) thisSignature = tuple(thisSignature) if thisSignature in thisMethod: raise OverloadedError("Overloaded method '%s' has a conflicting signature with another method." % name) thisMethod[thisSignature] = method storedOverloads[methodName] = thisMethod def typeCheck(self, *args, **keywargs): thisMethod = self._overloads[methodName] signature = tuple([type(arg) for arg in args]) realMethod = thisMethod.get(signature) if realMethod is None: raise OverloadedError("No overload matches '%s' signature for method '%s'." % (tuple(signature), attr)) return realMethod(self, *args, **keywargs) newDict[methodName] = typeCheck return type.__new__(cls, classname, bases, newDict) """ In your overloaded method declarations *all* arguments must be keyword arguments (except for self). Those default arguments are evaluated from left to right, arguments that are types are collected as the method signature until a non-type argument is found. For example, in the following 'example1' has a signature of (int, int, float), 'example2' has a signature of (int,). Calling 'example(3)' calls 'example2' under the hood. On the instance the method 'example' exists, but 'example1' and 'example2' don't : def example1(self, w=int, x=int, y=float, z=3) pass def example2(self, w=int) pass When calling the method the keywords must not be used. Untyped arguments *must* be passed in as keyword arguments. Not following this pattern will cause an exception to be raised *or* the wrong method to be called. This restriction can be removed, but only by annoyingly fiddly code, so it will have to wait for the next revision. Method attributes (like docstring etc) are not yet preserved. Add name mangling to '_overloads'. A maximum of ten overloads per method (0-9). Allow providing a default where no overloads match: a method with no typed arguments which is currently illegal. Add tuples of types. You can't have an overloaded method which takes no arguments. """