Feb-01-2020, 11:05 PM
(This post was last modified: Feb-02-2020, 08:34 AM by Gribouillis.)
The generic function mechanism (functools.singledispatch) allows us to select an action based on the type of the first argument of the action in the same way that polymorphism does with a class instance.
This post is a study on how we can use this existing mechanism to select an action based on the value of the argument, in the same way that
The solution here is to create a dictionary-like object named
Without further explanations, see by yourself if this code is understandable
This post is a study on how we can use this existing mechanism to select an action based on the value of the argument, in the same way that
switch ... case ... case ...
statements do in other languages. For example, we would like to have a different action depending on a certain variable having the value 'foo'
, 'bar'
or 'baz'
. But the singledispatch mechanism cannot do this because these three values have the same type!The solution here is to create a dictionary-like object named
CaseDispatcher
that creates a new type and a new instance of that type for each of the words 'foo', 'bar', 'baz'
. Based on the value of our variable, we can get this instance from the CaseDispatcher and select the action by the type of this instance.Without further explanations, see by yourself if this code is understandable
__version__ = '2020.02.02' import functools def _delegator(a, m, self): """Helper function for delegate_methods()""" return getattr(getattr(self, a), m) def _delegate_methods(cls, attr_name, method_names): """Add properties to a class that delegate to a member of the instance This function is a helper to build the CaseDispatcher class """ for m in method_names: f = functools.partial(_delegator, attr_name, m) setattr(cls, m, property(f)) class Missing: __slots__ = ('case',) def __init__(self, value): self.case = value class SubDict(dict): def __missing__(self, key): return Missing(key) class CaseDispatcher: """A dictionary-like storing unique instances of custom types. Usage: After s = CaseDispatcher(['foo', 'bar', 'baz']) s['foo'], s['bar'] and s['baz'] are all instances of newly created data types. These types can be used to create generic functions, for example: @singledispatch def greet(obj): print('Hi!') @greet.register(type(s['foo'])) def _(obj): print('Hi (foo version)!') @greet.register(type(s['bar'])) def _(obj): print('Hi (bar version)!') x = random.choice(['foo', 'bar', 'baz']) greet(s[x]) """ def __init__(self, keys=None): self.map = SubDict() if keys: self.add(keys) def add(self, keys): for x in keys: if x in self.map: continue self.map[x] = type('<{}>'.format(x), (), {'case': x, '__slots__': ()})() # add dictionary-like methods to the CaseDispatcher class. _delegate_methods(CaseDispatcher, 'map', ('__getitem__', 'get', 'items', 'keys', 'values', '__contains__', '__iter__', '__len__', '__delitem__')) if __name__ == '__main__': import random s = CaseDispatcher(['foo', 'bar', 'baz']) @functools.singledispatch def greet(obj): print('Hi!') @greet.register(type(s['foo'])) def _(obj): print('Hi (foo version)!') @greet.register(type(s['bar'])) def _(obj): print('Hi (bar version)!') for i in range(6): x = random.choice(['foo', 'bar', 'baz', 'qux']) print(x, 'chosen: ', end = '') greet(s[x])
Output:qux chosen: Hi!
foo chosen: Hi (foo version)!
bar chosen: Hi (bar version)!
foo chosen: Hi (foo version)!
foo chosen: Hi (foo version)!
baz chosen: Hi!