5 kyu

Function Overloading for Types

Description:

Typed Function overloading

Python is a very flexible language, however one thing it does not allow innately, is the overloading of functions. (With a few exceptions). For those interested, this existing kata explores the possibility of overloading object methods for different amounts of arguments. In this kata we are going to implement a form of overloading for any function/method, based on the classes of arguments, using decorators.

Overview

You will be writing a decorator @overload(*types) that will allow a function or method to be defined multiple times for different combinations of types of input arguments. The combination and order of classes of the arguments should call a specific defined function.

To keep this kata relatively simple, various aspects of functions (Going out of scope, etc) will not be tested, more details on what will and won't be tested are listed below.

Basic Example

@overload(int)
def analyse(x):
    print('X is an int!')
    
@overload(list)
def analyse(x):
    print('X is a list!')
    
analyse([])  # X is a list!
analyse(3)   # X is an int!

Additional Information:

The decorator

  • It should accept any type or class, even user made classes. Note that an inherited class does NOT count as its parent class. Each custom class should be considered unique.
  • It should be able to handle any number of arguments, including zero.
  • If an overloaded function/method is called without a matching set of types/classes, an Exception should be raised.
  • The decorator can be used on regular functions and on instance methods of a class as well (see below about overloading vs overwritting behaviours). 'Magic methods' (eg. __init__, __call__) will NOT be tested.

The decorated function or method

  • The function/method should return correct values.
  • All other usual function behaviours should work (Recursion, passing function as argument, etc).
  • Static and class methods will NOT be tested.
  • Note that when decorating an instance method, the type of self isn't provided to the decorator. The decorated function should yet be able to use the reference to self.
  • Varargs (*args) may be used in functions definitions. In that case, trust the types given to the decorator to decide what call must be mapped to that function.
  • You do not need to consider scope problems, like having 2 different functions with the same name but in 2 different scope. All functions will be defined as top level functions, or top level object methods.

Overloading vs overwritting behaviours

  • Multiple different overloaded functions/methods must be able to exist at a time: func(int,list) overloads a previous func(list) and doesn't overwrite it.
  • Functions and methods should be considered distinct, ie. analyse(int) and my_obj.analyse(int) are not the same and one shouldn't overwrite the other.
  • If a function/method with a "known" name is decorated again with an already existing types combination, the previous definition should be overwritten (See examples below).

More specific Examples

Should accept any class or type. Variable length args should work:

class My_Class: pass
class My_Inherited_Class(My_Class): pass
  
@overload(int, My_Class, str)
def some_function(*args):
    return 'Option 1'
    
@overload()
def some_function():
    return 'Option 2'
  
A = My_Class()
B = My_Inherited_Class()

some_function()               # Option 2
some_function(3, A, 'Words')  # Option 1 <= trust the types given to the decorator 
some_function('oops!')        # Exception raised <= no matching types combination
some_function(3, B, 'Words')  # Exception raised <= doesn't consider inheritence

Should be able to manage functions AND methods:

@overload(int)
def spin(x):
    return x*3-1
  
@overload(str)
def spin(x):
    return x[1:] + x[0]
      
print(spin(6))            # 17
print(spin('Hello'))      # elloH  <=  overload correctly, just like before...
print(spin(''))           # raise IndexError


class T:
    def __init__(self, x):
        self.val = x

    @overload(int)
    def spin(self, x):
        return self.val * x
      
    @overload(str)
    def spin(self, x):
        return x + str(self.val)
      
obj = T(7)
print(spin(6))            # 17 <= the instance method doesn't overwrite the function
print(obj.spin(2))        # 14 <= `self` is still usable

print(spin('Hello'))      # elloH
print(obj.spin('Hello'))  # Hello7

Previous definitions should be overwritten:

@overload(str)
def upgrade(x):
    print('First')
    
@overload(str)
def upgrade(x):
    print('Second')
    
upgrade('Y')    # Second

Good luck, and I hope you enjoy the Kata!

Decorator
Language Features
Metaprogramming

Stats:

CreatedFeb 11, 2021
PublishedFeb 12, 2021
Warriors Trained492
Total Skips12
Total Code Submissions804
Total Times Completed97
Python Completions97
Total Stars39
% of votes with a positive feedback rating91% of 33
Total "Very Satisfied" Votes28
Total "Somewhat Satisfied" Votes4
Total "Not Satisfied" Votes1
Total Rank Assessments4
Average Assessed Rank
5 kyu
Highest Assessed Rank
3 kyu
Lowest Assessed Rank
8 kyu
Ad
Contributors
  • Kacarott Avatar
  • Blind4Basics Avatar
Ad