【发布时间】:2021-09-06 21:25:04
【问题描述】:
这是我经常遇到的一个问题,我终于想找到最 Pythonic 的解决方案。其核心情况可描述如下:
- 一个类包含一些必须根据
__init__()参数计算或测试的属性。 - 类实例化后属性可能允许也可能不允许更改。
- 每次更改后必须重新计算/测试允许更改的属性。
- 类应该是“继承友好的”。
示例项目中的目标:帮助进行 2d 形状计算(例如多边形之间的最小距离或线之间的交点)的几何模块。线和多边形有很多共同的属性,所以创建一个 BaseGeometry 类,Line 和 Polygon 类继承自该类。 BaseGeometry 属性:
-
points一个 (n,x,y) 点列表,这些点在实例化时设置并且可以改变。每次设置时,必须将其断言为 numpy 数组。 -
domain:描述形状边界的 (xmin, xmax, ymin, ymax) 元组。每次设置points时都必须重新计算,但不得进行外部更改。
下面,我写了三个(简化的)解决方案来解决这个问题,都各有优缺点。
方法 1:错误地允许更改 domain 并且不清楚存在哪些类属性。
class BaseGeometry1:
def __init__(self, points):
self.set_points(points)
def set_points(self, points):
assert type(points) is np.ndarray
self.points = points
x, y = points.T
self.domain = ((min(x), max(x), min(y), max(y)))
方法 2:仍然允许更改 domain。清楚存在哪些属性,但感觉笨拙/不合逻辑。任意calculate_domain() 是否具有points 作为参数或仅使用self.points。每次更新 points 时都必须调用 calculate_domain()。
class BaseGeometry2:
def __init__(self, points):
self.points = self.set_points(points)
self.domain = self.calculate_domain()
def set_points(self, points):
assert type(points) is np.ndarray
return points
def calculate_domain(self):
x, y = self.points.T
return (min(x), max(x), min(y), max(y))
方法 3:我觉得这是在正确的轨道上,但我仍然不确定结构。 points.setter 也设置了 _domain 感觉很奇怪。另外,只对所有类属性使用 @property 装饰器是不好的做法吗?
class BaseGeometry3:
def __init__(self, points):
self.points = points
@property
def points(self):
return self._points
@points.setter
def points(self, points):
assert type(points) is np.ndarray
self._points = points
x, y = points.T
self._domain = ((min(x), max(x), min(y), max(y)))
@property
def domain(self):
return self._domain
我的问题如下:
- 这些方法之一是否被认为是传统方法?为什么/为什么不?
- 在解决此问题时,还需要注意哪些其他约定?
- 还有哪些其他提示或资源可以帮助我改进课堂结构?
- 继承自 GeometryBase3 的类是否必须完全重新定义
points.setter方法才能引入一些从points计算的新属性?
另外,我刚开始在这里提问,因此也欢迎对帖子提出任何反馈。提前感谢您的时间和任何答案!
亲切的问候,
约斯特
【问题讨论】:
-
我会选择第三种方法。如果计算
domain的成本很高,并且points的设置频率会比domain的设置频率高,那么您可以在points的setter 中将_domain设置为None,然后计算并赋值仅当domain的 getter 请求时。您无需为任何顶级功能重新定义points的设置器。 -
欢迎来到 Stack Overflow。请查看tour 页面以获取有关询问的信息。具体来说,问题应该集中在一个的事情上,否则你可能会得到几个不同的答案,它们都同样只解决了你问题的一部分。同样,Stack Overflow 不适合基于意见/主观问题,其中包括最佳实践建议、约定、提示请求等。
-
最后,像“提前感谢”或签名这样的元评论是不必要的;您通过支持有用的答案/cmets 并接受最能解决您的问题/回答您的问题的答案来感谢这里的人们。同样,每个问题都使用帖子右下角的用户卡签名,其中包括作者姓名、代表、时间戳等。
-
感谢您的提示和解释!关于意见/约定规则:我完全理解这里要避免基于意见的问题。但是,我觉得代码结构和样式是“好”编程的主要部分(至少对我而言)是我花在思考代码上的大部分时间的原因。我渴望在这方面做得更好,但事实上有许多有效的方法正是这感觉如此困难的原因。堆栈溢出是否根本不是就该主题进行公开讨论的最佳场所,还是我应该用不同的措辞来表达我的问题?
标签: python inheritance properties attributes structure