Inspired by Muhammad Alkarouri answer in What are good uses for Python3's "Function Annotations" , I want to do this multimethod
for methods, not regular functions. However, when I do this
registry = {}
class MultiMethod(object):
def __init__(self, name):
self.name = name
self.typemap = {}
def __call__(self, *args):
types = tuple(arg.__class__ for arg in args) # a gen开发者_如何学JAVAerator expression!
function = self.typemap.get(types)
if function is None:
raise TypeError("no match")
return function(*args)
def register(self, types, function):
if types in self.typemap:
raise TypeError("duplicate registration")
self.typemap[types] = function
def multimethod(function):
name = function.__name__
mm = registry.get(name)
if mm is None:
mm = registry[name] = MultiMethod(name)
types = tuple(function.__annotations__.values())
mm.register(types, function)
return mm
class A:
@multimethod
def foo(self, a: int):
return "an int"
a = A()
print( a.foo( 1 ) )
I got this:
Traceback (most recent call last):
File "test.py", line 33, in <module>
print( a.foo( 1 ) )
File "test.py", line 12, in __call__
return function(*args)
TypeError: foo() takes exactly 2 arguments (1 given)
Which seems to be expected, as explained in Decorating a method , because of the self
argument.
But I have no idea how to make it work. Well, when I remove the "self", it's working (almost) fine, but I don't want to remove it. Please note, that I'm doing this for practice, I know that there are some libs, providing method overloading.
What I tried:
very silly, but wanted to try - added parameter
self
indef multimethod( function )
- the same errorI thought about adding in the
__init__
ofclass MultiMethod
a third parameter -obj
and storedself
as member, but I can't do this throughmultimethod
as it is a function.I don't want to add parameters for the decorator, so this options (if possible at all) is ignored
I read several similar questions, but didn't find what I was looking for. I'm pretty sure this is dummy question, but I ran out of ideas.
The basic problem you have is that you use a class in place of a function. There is no mechanism to bind that class to the instance it's called from, unlike a function where this happens automatically.
In short, when you do a.foo( .. )
it returns a MultiMethod
, but this object has no idea that it is supposed to be bound to a
.
You have to pass in the instance in some way or another. One easy way is to wrap it all in a function and let Python do it's thing:
registry = {}
class MultiMethod(object):
def __init__(self, name):
self.name = name
self.typemap = {}
# self = a MultiMethod instance, instance = the object we want to bind to
def __call__(self, instance, *args):
types = tuple(arg.__class__ for arg in args) # a generator expression!
function = self.typemap.get(types)
if function is None:
raise TypeError("no match")
return function(instance, *args)
def register(self, types, function):
if types in self.typemap:
raise TypeError("duplicate registration")
self.typemap[types] = function
def multimethod(function):
name = function.__name__
mm = registry.get(name)
if mm is None:
mm = registry[name] = MultiMethod(name)
types = tuple(function.__annotations__.values())
mm.register(types, function)
# return a function instead of a object - Python binds this automatically
def getter(instance, *args, **kwargs):
return mm(instance, *args, **kwargs)
return getter
class A:
@multimethod
def foo(self, a: int):
return "an int", a
a = A()
print( a.foo( 1 ) )
The more complex way would be to write your own descriptor on the A
class that does this binding.
精彩评论