Overloading (or alternatives) in Python API design

后端 未结 5 1974
半阙折子戏
半阙折子戏 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:47

    There are a couple of options.

    You can have one constructor that accepts and arbitrary number of arguments (with *args and/or **varargs syntaxes) and does different things depending on the number and type the arguments have.

    Or, you can write secondary constructors as class methods. These are known as "factory" methods. If you have multiple constructors that take the same number of objects of the same classes (as in your BezierCurve example), this is probably your only option.

    If you don't mind overriding __new__ rather than __init__, you can even have both, with the __new__ method handling one form of arguments by itself and referring other kinds to the factory methods for regularizing. Here's an example of what that might look like, including doc strings for the multiple signatures to __new__:

    class Circle(object):
        """Circle(center, radius) -> Circle object
           Circle(point1, point2, point3) -> Circle object
           Circle(curve1, curve2, curve3) -> Circle object
    
           Return a Circle with the provided center and radius. If three points are given,
           the center and radius will be computed so that the circle will pass through each
           of the points. If three curves are given, the circle's center and radius will
           be chosen so that the circle will be tangent to each of them."""
    
        def __new__(cls, *args):
            if len(args) == 2:
                self = super(Circle, cls).__new__(cls)
                self.center, self.radius = args
                return self
            elif len(args) == 3:
                if all(isinstance(arg, Point) for arg in args):
                    return Circle.through_points(*args)
                elif all(isinstance(arg, Curve) for arg in args):
                    return Circle.tangent_to_curves(*args)
            raise TypeError("Invalid arguments to Circle()")
    
        @classmethod
        def through_points(cls, point1, point2, point3):
            """from_points(point1, point2, point3) -> Circle object
    
            Return a Circle that touches three points."""
    
            # compute center and radius from the points...
            # then call back to the main constructor:
            return cls(center, radius)
    
        @classmethod
        def tangent_to_curves(cls, curve1, curve2, curve3):
            """from_curves(curve1, curve2, curve3) -> Circle object
    
            Return a Circle that is tangent to three curves."""
    
            # here too, compute center and radius from curves ...
            # then call back to the main constructor:
            return cls(center, radius)
    

提交回复
热议问题