PythonAPI设计中的重载(或替代方法)

2024-05-15 17:43:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个很大的现有程序库,目前有一个.NET绑定,我正在考虑编写一个Python绑定。现有的API广泛使用基于签名的重载。因此,我收集了大量静态函数,例如:

Circle(p1, p2, p3) -- Creates a circle through three points
Circle(p, r)       -- Creates a circle with given center point and radius
Circle(c1, c2, c3) -- Creates a circle tangent to three curves

在一些情况下,相同的输入必须以不同的方式使用,因此基于签名的重载不起作用,因此我不得不使用不同的函数名。例如

^{pr2}$

我想第二种技术(使用不同的函数名)可以在pythonapi中的任何地方使用。所以,我会的

CircleThroughThreePoints(p1, p2, p3)
CircleCenterRadius(p, r)
CircleTangentThreeCurves(c1, c2, c3)

但是这些名称看起来冗长得令人不快(我不喜欢缩写),而且发明所有这些名称将是一个相当大的挑战,因为这个库有数千个函数。在

低优先级:
努力(就我而言)--我不在乎是否要写很多代码。
性能

高优先级:
方便呼叫者使用/理解(许多人将是编程新手)。
我很容易写出好的文档。
简单——避免在调用方代码中使用高级概念。在

我确信我不是第一个希望在Python中实现基于签名的重载的人。人们通常使用哪些变通方法?在


Tags: 函数代码名称net程序库threep2c2
3条回答

你可以用字典,像这样

Circle({'points':[p1,p2,p3]})
Circle({'radius':r})
Circle({'curves':[c1,c2,c3])

初始化者会说

^{pr2}$

一种选择是在构造函数中独占关键字参数,并包含逻辑来确定应该使用什么:

class Circle(object):
    def __init__(self, points=(), radius=None, curves=()):
        if radius and len(points) == 1:
            center_point = points[0]
            # Create from radius/center point
        elif curves and len(curves) == 3:
            # create from curves
        elif points and len(points) == 3:
            # create from points
        else:
            raise ValueError("Must provide a tuple of three points, a point and a radius, or a tuple of three curves)

您还可以使用classmethods为API用户简化操作:

^{pr2}$

用法:

c = Circle.from_points(p1, p2, p3)
c = Circle.from_point_and_radius(p1, r)
c = Circle.from_curves(c1, c2, c3)

有几个选择。在

您可以有一个构造函数,它接受任意数量的参数(使用*args和/或**varargs语法),并根据参数的数量和类型执行不同的操作。在

或者,您可以将二级构造函数编写为类方法。这些被称为“工厂”方法。如果有多个构造函数使用相同数量的相同类的对象(如您的BezierCurve示例),这可能是您唯一的选择。在

如果您不介意重写__new__而不是__init__,您甚至可以同时使用这两种方法,__new__方法本身处理一种形式的参数,并将其他类型引用到工厂方法进行正则化。下面是一个可能的示例,包括__new__的多个签名的doc字符串:

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)

相关问题 更多 >