1

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

例如,考虑基类Shape,它只是一个坐标列表的容器:

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类,其中points.setter方法检查sequence的长度为 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

另一种方法是定义一个类属性_NPOINTS并将npoints 属性更改Shape

@property
def npoints(self):
    return _NPOINTS

但是,我失去了使用抽象基类的好处,因为:

  • 我看不到如何使用type, 和
  • 我不知道如何定义抽象类属性。

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

4

1 回答 1

3

在不了解您的项目的情况下,我无法就总体设计提供具体建议。我将提供一些更一般的提示和想法。

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

  2. 您通常不使用抽象基类,而是简单地记录预期的接口,而不是编写符合该接口的类。由于 Python 的动态特性,您并不需要一个通用的基类。公共基类通常还有其他优点——例如共享功能。

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

于 2012-06-12T13:51:36.973 回答