Overloading (or alternatives) in Python API design

后端 未结 5 1973
半阙折子戏
半阙折子戏 2021-01-03 02:07

I have a large existing program library that currently has a .NET binding, and I\'m thinking about writing a Python binding. The existing API makes extensive use of signatur

5条回答
  •  自闭症患者
    2021-01-03 02:35

    There are a number of modules in PyPI that can help you with signature based overloading and dispatch: multipledispatch, multimethods, Dispatching - none of which I have real experience with, but multipledispatch looks like what you want and it's well documented. Using your circle example:

    from multipledispatch import dispatch
    
    class Point(tuple):
        pass
    
    class Curve(object):         
        pass
    
    @dispatch(Point, Point, Point)
    def Circle(point1, point2, point3):
        print "Circle(point1, point2, point3): point1 = %r, point2 = %r, point3 = %r" % (point1, point2, point3)
    
    @dispatch(Point, int)
    def Circle(centre, radius):
        print "Circle(centre, radius): centre = %r, radius = %r" % (centre, radius)
    
    @dispatch(Curve, Curve, Curve)
    def Circle(curve1, curve2, curve3):
        print "Circle(curve1, curve2, curve3): curve1 = %r, curve2 = %r, curve3 = %r" % (curve1, curve2, curve3)
    
    
    >>> Circle(Point((10,10)), Point((20,20)), Point((30,30)))
    Circle(point1, point2, point3): point1 = (10, 10), point2 = (20, 20), point3 = (30, 30)
    >>> p1 = Point((25,10))
    >>> p1
    (10, 10)
    >>> Circle(p1, 100)
    Circle(centre, radius): centre = (25, 10), radius = 100
    
    >>> Circle(*(Curve(),)*3)
    Circle(curve1, curve2, curve3): curve1 = <__main__.Curve object at 0xa954d0>, curve2 = <__main__.Curve object at 0xa954d0>, curve3 = <__main__.Curve object at 0xa954d0>
    
    >>> Circle()
    Traceback (most recent call last):
      File "", line 1, in 
      File "/home/mhawke/virtualenvs/urllib3/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 143, in __call__
        func = self.resolve(types)
      File "/home/mhawke/virtualenvs/urllib3/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 184, in resolve
        (self.name, str_signature(types)))
    NotImplementedError: Could not find signature for Circle: <>
    

    It's also possible to decorate instance methods, so you can provide multiple implementations of __init__(), which is quite nice. If you were implementing any actual behaviour within the class, e.g. Circle.draw(), you would need some logic to work out what values are available with to draw the circle (centre and radius, 3 points, etc). But as this is just to provide a set of bindings, you probably only need to call the correct native code function and pass on the parameters :

    from numbers import Number
    from multipledispatch import dispatch
    
    class Point(tuple):
        pass
    
    class Curve(object):
        pass
    
    class Circle(object):
        "A circle class"
    
        # dispatch(Point, (int, float, Decimal....))
        @dispatch(Point, Number)
        def __init__(self, centre, radius):
            """Circle(Point, Number): create a circle from a Point and radius."""
    
            print "Circle.__init__(): centre %r, radius %r" % (centre, radius)
    
        @dispatch(Point, Point, Point)
        def __init__(self, point1, point2, point3):
            """Circle(Point, Point, Point): create a circle from 3 points."""
    
            print "Circle.__init__(): point1 %r, point2 %r, point3 = %r" % (point1, point2, point3)
    
        @dispatch(Curve, Curve, Curve)
        def __init__(self, curve1, curve2, curve3):
            """Circle(Curve, Curve, Curve): create a circle from 3 curves."""
    
            print "Circle.__init__(): curve1 %r, curve2 %r, curve3 = %r" % (curve1, curve2, curve3)
    
        __doc__ = '' if __doc__ is None else '{}\n\n'.format(__doc__)
        __doc__ += '\n'.join(f.__doc__ for f in __init__.funcs.values())
    
    
    >>> print Circle.__doc__
    A circle class
    
    Circle(Point, Number): create a circle from a Point and radius.
    Circle(Point, Point, Point): create a circle from 3 points.
    Circle(Curve, Curve, Curve): create a circle from 3 curves.
    
    >>> for num in 10, 10.22, complex(10.22), True, Decimal(100):
    ...     Circle(Point((10,20)), num)
    ... 
    Circle.__init__(): centre (10, 20), radius 10
    <__main__.Circle object at 0x1d42fd0>
    Circle.__init__(): centre (10, 20), radius 10.22
    <__main__.Circle object at 0x1e3d890>
    Circle.__init__(): centre (10, 20), radius (10.22+0j)
    <__main__.Circle object at 0x1d42fd0>
    Circle.__init__(): centre (10, 20), radius True
    <__main__.Circle object at 0x1e3d890>
    Circle.__init__(): centre (10, 20), radius Decimal('100')
    <__main__.Circle object at 0x1d42fd0>
    
    >>> Circle(Curve(), Curve(), Curve())
    Circle.__init__(): curve1 <__main__.Curve object at 0x1e3db50>, curve2 <__main__.Curve object at 0x1d42fd0>, curve3 = <__main__.Curve object at 0x1d4b1d0>
    <__main__.Circle object at 0x1d4b4d0>
    
    >>> p1=Point((10,20))
    >>> Circle(*(p1,)*3)
    Circle.__init__(): point1 (10, 20), point2 (10, 20), point3 = (10, 20)
    <__main__.Circle object at 0x1e3d890>
    
    >>> Circle()
    Traceback (most recent call last):
      File "", line 1, in 
      File "/home/mhawke/virtualenvs/urllib3/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 235, in __call__
        func = self.resolve(types)
      File "/home/mhawke/virtualenvs/urllib3/lib/python2.7/site-packages/multipledispatch/dispatcher.py", line 184, in resolve
        (self.name, str_signature(types)))
    NotImplementedError: Could not find signature for __init__: <>
    

提交回复
热议问题