我需要处理一个类的两个对象,它将返回同一个类的第三个对象,我正试图确定是否将它作为一个独立的函数来处理,该函数接收两个对象并返回第三个对象,还是作为一个方法来获取另一个对象并返回第三个对象。在
举个简单的例子。这会不会:
from collections import namedtuple
class Point(namedtuple('Point', 'x y')):
__slots__ = ()
#Attached to class
def midpoint(self, otherpoint):
mx = (self.x + otherpoint.x) / 2.0
my = (self.y + otherpoint.y) / 2.0
return Point(mx, my)
a = Point(1.0, 2.0)
b = Point(2.0, 3.0)
print a.midpoint(b)
#Point(x=1.5, y=2.5)
或者这个:
^{pr2}$为什么一个会比另一个更受欢迎呢?在
这似乎远没有我问这个问题时预想的那么明确。在
总而言之,像a.midpoint(b)这样的东西似乎并不可取,因为它似乎给一个点或另一个点赋予了一个特殊的位置,而这个对称函数实际上是返回一个全新的点实例的对称函数。但它似乎很大程度上是一个品味和风格的问题,比如一个独立的模块函数或一个附加到类的函数,但并不意味着被疯狂的人调用,比如点.中点(a、b)。在
我个人认为,我个人倾向于独立的模块功能,但这可能取决于具体情况。如果函数肯定与类紧密绑定,并且存在名称空间污染或潜在混淆的风险,那么创建类函数可能更有意义。在
另外,一些人提到要使函数更通用,可能是通过实现类的附加特性来支持这一点。在这个处理点和中点的特殊情况下,这可能是最好的方法。它支持多态性和代码重用,可读性强。不过,在很多情况下,这是行不通的(比如这个项目激发了我去问这个问题),但是点和中点似乎是一个简洁易懂的例子来说明这个问题。在
谢谢大家,很有启发性。在
在这种情况下,可以使用运算符重载:
我认为决定主要取决于函数的一般性和抽象性。如果您可以在实现一个小的干净接口集的所有对象上运行函数,那么就可以将它转换为一个单独的函数。你的函数依赖的接口越多,接口越具体,把它放在类上就越有意义(因为这个类的实例很可能是函数唯一可以使用的对象)。在
我会选择第二种选择,因为在我看来,它比第一种更清楚。您正在两点之间执行中点操作,而不是在某个点上执行中点操作。类似地,这个接口的自然扩展可以定义
dot
、cross
、magnitude
、average
、median
等。其中一些函数将对Points
进行操作,而其他函数则可以对列表进行操作。使其成为一个函数可以使它们都具有一致的接口。在将它定义为函数还允许它与呈现
.x
.y
接口的任何一对对象一起使用,而使其成为一个方法需要这两个对象中至少有一个是Point
。在最后,为了解决函数的位置问题,我认为将它与Point类放在同一个包中是有意义的。这将它放在同一个命名空间中,这清楚地表明了它与
Point
的关系,而且在我看来,它比静态或类方法更具python性。在更新: 进一步阅读
@staticmethod
vs包/模块的pythonicity:在thomaswouter对问题What is the difference between staticmethod and classmethod in Python的回答和Mike Steder对init and arguments in Python的回答中,作者指出相关函数的包或模块可能是更好的解决方案。托马斯·伍特说:
迈克·斯特德评论道:
然而,codeape在下面正确地指出,} 。在
Point.midpoint(a,b)
的调用约定将与类型一起定位功能。BDFL似乎也值@staticmethod
,因为__new__
方法是^{出于上述原因,我个人倾向于使用一个函数,但似乎在
@staticmethod
和独立函数之间的选择很大程度上取决于旁观者。在第一种方法是合理的,在概念上与集合.联合和集合交集做。任何
func(Point, Point) --> Point
都明显与Point类相关,因此不存在干扰类的统一性或内聚性的问题。在如果涉及不同的类,这将是一个更艰难的选择:
draw_perpendicular(line, point) --> line
。要解决类的选择问题,您可以选择具有最相关逻辑的类。例如,结构连接需要字符串分隔符和字符串列表。它可以是一个独立的函数(就像以前使用string模块时一样),也可以是列表上的方法(但它只适用于字符串列表),或者是字符串上的方法。选择后者是因为连接更多的是字符串而不是列表。尽管这个选择导致了可以说是尴尬的表达delimiter.join(things_to_join)
,但还是做出了这个选择。在我不同意另一个建议使用类方法的受访者。它们通常用于替代构造函数签名,但不用于类实例的转换。例如,日期时间.fromordinal是一个类方法,用于从类的实例(在本例中,是从int中)构造日期的类方法。这与日期时间.替换这是根据现有实例生成新的datetime实例的常规方法。这将使您避免使用classmethod进行中点计算。在
另一个想法是:如果在Point()类中保留midpoint(),则可以创建具有相同Point API但内部表示不同的其他类(即极坐标对于某些类型的工作可能比笛卡尔坐标更方便)。如果midpoint()是一个单独的函数,那么您将开始失去封装和一致接口的好处。在
相关问题 更多 >
编程相关推荐