Python 类工厂和抽象基类

Python 类工厂和抽象基类,python,abstract-class,class-factory,Python,Abstract Class,Class Factory,我试图基于抽象基类定义一些类。这些类中的每一个基本上都定义了可视化包的单元形状。单元由多个顶点(点)组成,每个子类需要不同数量的点。每个类都可以看作是固定数量点坐标的容器 作为一个例子,考虑基类形状,它只是一个坐标列表的容器: class Shape(object): """Cell shape base class.""" def __init__(self, sequence): self.points = sequence @property

我试图基于抽象基类定义一些类。这些类中的每一个基本上都定义了可视化包的单元形状。单元由多个顶点(点)组成,每个子类需要不同数量的点。每个类都可以看作是固定数量点坐标的容器

作为一个例子,考虑基类<代码>形状<代码>,它只是一个坐标列表的容器:

class Shape(object):
    """Cell shape base class."""
    def __init__(self, sequence):
        self.points = sequence

    @property
    def points(self):
        return self._points

    @points.setter
    def points(self, sequence):
        # Error checking goes here, e.g. check that `sequence` is a
        # sequence of numeric values.
        self._points = sequence
理想情况下,我希望能够定义一个
Square
类,其中
指向.setter
方法检查
序列的长度是否为4。此外,我希望用户不能实例化
Shape
。有没有一种方法可以将
Shape
定义为抽象基类?我已尝试将形状的定义更改为以下内容:

import abc

class Shape(object):
    """Cell shape base class."""

    __metaclass__ = abc.ABCMeta

    def __init__(self, sequence):
        self.points = sequence

    @abc.abstractproperty
    def npoints(self):
        pass

    @property
    def points(self):
        return self._points

    @points.setter
    def points(self, sequence):
        # Error checking goes here...
        if len(sequence) != self.npoints:
            raise TypeError('Some descriptive error message!')

        self._points = sequence
这需要子类来定义属性
npoints
。然后我可以将类
Square
定义为

class Square(Shape):
    @property
    def npoints(self):
        return 4
但是,对于大量的子案例(并且要实现多个属性),这将是相当繁琐的。我希望定义一个类工厂,为我创建子类,大致如下:

def Factory(name, npoints):
    return type(name, (Shape,), dict(npoints=npoints))

Triangle = Factory('Triangle', 3)    
Square = Factory('Square', 4)
# etc...
这个类工厂函数是一种有效的方法,还是我正在破坏
npoints
属性?是否最好将对
type
的调用替换为更详细的内容,如:

def Factory(name, _npoints):
    class cls(Shape):
        @property
        def npoints(self):
            return _npoints
    cls.__name__ = name
    return cls
另一种方法是定义类属性
\u NPOINTS
并更改
NPOINTS
形状的属性

@property
def npoints(self):
    return _NPOINTS
但是,我失去了使用抽象基类的好处,因为:

  • 我看不出如何使用
    type
    定义类属性,以及
  • 我不知道如何定义抽象类属性

有没有人对实现这个抽象基类和类工厂函数的最佳方法有什么想法,或者甚至对一个更好的设计有什么想法?

如果对您的项目没有更多了解,我无法给出关于一般设计的具体建议。我将只提供一些更一般的提示和想法

  • 动态生成的类通常表明您根本不需要单独的类——只需编写一个包含所有功能的类即可。在实例化时获取属性的
    Shape
    类有什么问题?(当然,使用动态生成的类是有原因的,
    namedtuple()
    factory函数就是一个例子。但是,我在您的问题中找不到任何具体原因。)

  • 与使用抽象基类不同,您通常只需编写预期接口的文档,然后编写符合此接口的类。由于Python的动态特性,您不需要严格地使用公共基类。公共基类通常还有其他优点,例如共享功能

  • 如果不这样做会在不相关的地方导致奇怪的错误,那么只检查应用程序代码错误。如果,比如说,您的函数需要一个iterable,只需假设您得到了一个iterable。如果用户传入了其他内容,那么代码在尝试迭代传入的对象时将失败,并且错误消息通常足以让应用程序开发人员理解错误


  • 这一切看起来都很像Java:抽象类、getter和setter、类型检查等等。虽然可以在Python中完成这些工作,但通常不需要。只有在对getter和setter有实际好处的情况下才编写getter和setter,并且只有在必要时才检查类型——否则依赖于duck类型。抽象基类不应该使用得太普遍。@SvenMarnach我同意duck类型在Python中感觉更自然,我在应用程序的其他部分也非常依赖它。然而,我觉得从一个公共基类派生在这里是有益的——我需要根据可视化包规范定义给定数量的单元类型。规范只允许具有一定数量点的序列。通过定义一些像我在问题中尝试的类(使用其他方法,如
    \uuu iter\uuuu
    ),我确保我的应用程序的用户可以轻松创建符合规范的序列。如果数据不符合规范,会发生什么?可视化框架不会给出错误吗?为什么不简单地依赖于这些错误呢?
    \uuuu init
    应该是
    \uuuu init\uuuu
    。@SvenMarnach我可以,我也可以。我试图在这里定义的类是方便类,帮助用户创建符合规范的对象,这样他们就不必自己构造它们。我试图创建的类比我问题中的类更复杂,这只是一个很小的例子。我的大部分代码都是对duck类型的回复,在这里我创建了助手类,我可以告诉用户,如果在我的应用程序中传递,肯定不会出现错误。谢谢你的评论。