Where to put a function that acts on two instances of a specific class

Viewed 66

This is really a design question and I would like to know a bit of what design patterns to use.

I have a module, let's say curves.py that defines a Bezier class. Then I want to write a function intersection which uses a recursive algorithm to find the intersections between two instances of Bezier.

What options do I have for where to put this functions? What are some best practices in this case? Currently I have written the function in the module itself (and not as a method to the class).

So currently I have something like:

def intersections(inst1, inst2): ...

def Bezier(): ... 

and I can call the function by passing two instances:

from curves import Bezier, intersections

a = Bezier()
b = Bezier()

result = intersections(a, b)

However, another option (that I can think of) would be to make intersection a method of the class. In this case I would instead use

a.intersections(b) 

For me the first choice makes a bit more sense since it feels more natural to call intersections(a, b) than a.intersections(b). However, the other option feels more natural in the sense that the function intersection really only acts on Bezier instances and this feels more encapsulated.

Do you think one of these is better than the other, and in that case, for what reasons? Are there any other design options to use here? Are there any best practices?

1 Answers

As an example, you can compare how the builtin set class does this:

intersection(*others)
set & other & ...
Return a new set with elements common to the set and all others.

So intersection is defined as a regular instance method on the class that takes another (or multiple) sets and returns the intersection, and it can be called as a.intersection(b).

However, due to the standard mechanics of how instance methods work, you can also spell it set.intersection(a, b) and in practice you'll see this quite often since like you say it feels more natural.

You can also override the __and__ method so this becomes available as a & b.

In terms of ease of use, putting it on the class is also friendlier, because you can just import the Bezier class and have all associated features available automatically, and they're also discoverable via help(Bezier).

Related