On Sun, 25 Sep 2005 09:30:30 +0100, Catalin Marinas <[EMAIL PROTECTED]> wrote:
>Hi, > >Sorry if this was previously discussed but it's something I miss in >Python. I get around this using isinstance() but it would be cleaner >to have separate functions with the same name but different argument >types. I think the idea gets quite close to the Lisp/CLOS >implementation of methods. > >Below is just simple implementation example (and class functions are >not supported) but it can be further extended/optimised/modified for >better type detection like issubclass() etc. The idea is similar to >the @accepts decorator: > > >methods = dict() > >def method(*types): > def build_method(f): > assert len(types) == f.func_code.co_argcount > > if not f.func_name in methods: > methods[f.func_name] = dict() > methods[f.func_name][str(types)] = f > > def new_f(*args, **kwds): > type_str = str(tuple([type(arg) for arg in args])) > assert type_str in methods[f.func_name] > return methods[f.func_name][type_str](*args, **kwds) > new_f.func_name = f.func_name > > return new_f > > return build_method > > >And its utilisation: > >@method(int) >def test(arg): > print 'int', arg > >@method(float) >def test(arg): > print 'float', arg > >test(1) # succeeds >test(1.5) # succeeds >test(1, 2) # assert fails >test('aaa') # assert fails > > >Let me know what you think. Thanks. > I am reminded of reinventing multimethods, but an interesting twist, so I'll add on ;-) The following could be made more robust, but avoids a separately named dictionary and lets the function name select a dedicated-to-the-function-name dictionary (subclass) directly instead of looking it up centrally with two levels involved. Just thought of this variant of your idea, so not tested beyond what you see ;-) >>> def method(*types): ... def mkdisp(f): ... try: disp = eval(f.func_name) ... except NameError: ... class disp(dict): ... def __call__(self, *args): ... return self[tuple((type(arg) for arg in args))](*args) ... disp = disp() ... disp[types] = f ... return disp ... return mkdisp ... >>> @method(int) ... def test(arg): ... print 'int', arg ... >>> test {(<type 'int'>,): <function test at 0x02EEAE2C>} >>> @method(float) ... def test(arg): ... print 'float', arg ... >>> test(1) int 1 >>> test(1.5) float 1.5 >>> test(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 7, in __call__ KeyError: (<type 'int'>, <type 'int'>) >>> test('aaa') Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 7, in __call__ KeyError: <type 'str'> >>> test {(<type 'int'>,): <function test at 0x02EEAE2C>, (<type 'float'>,): <function test at 0x02EEAE64>} You could give it a nice __repr__ ... Hm, I'll just cheat right here instead of putting it in the decorator's class where it belongs: >>> def __repr__(self): ... fname = self.values()[0].func_name ... types = [tuple((t.__name__ for t in sig)) for sig in self.keys()] ... return '<%s-disp for args %s>' % (fname, repr(types)[1:-1]) ... >>> type(test).__repr__ = __repr__ >>> test <test-disp for args ('int',), ('float',)> >>> @method(str, str) ... def test(s1, s2): return s1, s2 ... >>> test <test-disp for args ('int',), ('str', 'str'), ('float',)> >>> test('ah', 'ha') ('ah', 'ha') >>> test(123) int 123 That __repr__ could definitely be improved some more ;-) Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list