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类型的回复,在这里我创建了助手类,我可以告诉用户,如果在我的应用程序中传递,肯定不会出现错误。谢谢你的评论。