I need to deal with a two objects of a class in a way that will return a third object of the same class, and I am trying to determine whether it is better to do this as an independent function that receives two objects and returns the third or as a method which would take one other object and return the third.
For a simple example. Would this:
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)
Or this:
from collections import namedtuple
class Point(namedtuple('Point', 'x y')):
__slots__ = ()
#not attached to class
#takes two point objects
def midpoint(p1, p2):
mx = (p1.x + p2.x) / 2.0
my = (p1.y + p2.y) / 2.0
return Point(mx, my)
a = Point(1.0, 2.0)
b = Point(2.0, 3.0)
print midpoint(a, b)
#Point(x=1.5, y=2.5)
and why would one be preferred over the other?
This seems far less clear cut than I had expected when I asked the question.
In summary, it seems that something like a.midpoint(b) is not preferred since it seems to give a special place to one point or another in what is really a symmetric function that returns a completely new point instance. But it seems to be largely a matter of taste and style between something like a freestanding module function or a function attached to the class, but not meant to be called by the insance, such as Point.midpoint(a, b).
I think, personally, I stylistically lean towards free-standing module functions, but it may depend on the circumstances. In cases where the function is definitely tightly bound to the class and there is any risk of namespace pollution or potential confusion, then making a class function probably makes more sense.
Also, a couple of people mentioned making the function more general, perhaps by implementing additional features of the class to support this. In this particular case dealing with points and midpoints, that is probably the overall best approach. It supports polymorphism and code reuse and is highly readable. In a lot of cases though, that would not work (the project that inspired me to ask this for instance), but points and midpoints seemed like a concise and understandable example to illustrate the question.
Thank you all, it was enlightening.