jupiterbjy
jupiterbjy

Reputation: 3530

Using singledispatch with custom class(CPython 3.8.2)

Let's say I want to set functions for each classes in module Named 'MacroMethods'. So I've set up singledispatch after seeing it in 'Fluent Python' like this:

@singledispatch
def addMethod(self, obj):
    print(f'Wrong Object {str(obj)} supplied.')
    return obj

...

@addMethod.register(MacroMethods.Wait)
def _(self, obj):
    print('adding object wait')
    obj.delay = self.waitSpin.value
    obj.onFail = None
    obj.onSuccess = None
    return obj

Desired behavior is - when instance of class 'MacroMethods.Wait' is given as argument, singledispatch runs registered function with that class type.

Instead, it runs default function rather than registered one.

>>> Wrong Object <MacroMethods.Wait object at 0x0936D1A8> supplied.

However, type() clearly shows instance is class 'MacroMethods.Wait', and dict_keys property also contains it.

>>> dict_keys([<class 'object'>, ..., <class 'MacroMethods.Wait'>])

I suspect all custom classes I made count as 'object' type and don't run desired functions in result.

Any way to solve this problem? Entire codes are here.


Update

I've managed to mimic singledispatch's actions as following:

from functools import wraps


def state_deco(func_main):
    """
    Decorator that mimics singledispatch for ease of interaction expansions.
    """

    # assuming no args are needed for interaction functions.
    func_main.dispatch_list = {}  # collect decorated functions

    @wraps(func_main)
    def wrapper(target):
        # dispatch target to destination interaction function.
        nonlocal func_main
        try:
            # find and run callable for target
            return func_main.dispatch_list[type(target)]()
        except KeyError:
            # If no matching case found, main decorated function will run instead.
            func_main()

    def register(target):
        # A decorator that register decorated function to main decorated function.
        def decorate(func_sub):
            nonlocal func_main
            func_main.dispatch_list[target] = func_sub

            def register_wrapper(*args, **kwargs):
                return func_sub(*args, **kwargs)

            return register_wrapper
        return decorate

    wrapper.register = register
    return wrapper

Used like:

@state_deco
def general():
    return "A's reaction to undefined others."

@general.register(StateA)
def _():
    return "A's reaction of another A"

@general.register(StateB)
def _():
    return "A's reaction of B"

But still it's not singledispatch, so I find this might be inappropriate to post this as answer.

Upvotes: 1

Views: 728

Answers (1)

brocla
brocla

Reputation: 187

I wanted to do similar and had the same trouble. Looks like we have bumped into a python bug. Found a write-up that describes this situation. Here is the link to the Python Bug Tracker.

Python 3.7 breaks on singledispatch_function.register(pseudo_type), which Python 3.6 accepted

Upvotes: 1

Related Questions