pythonencapsulationmember-functionsnon-member-functions

Non-member vs member functions in Python


I'm relatively new to Python and struggling to reconcile features of the language with habits I've picked up from my background in C++ and Java.

The latest issue I'm having has to do with encapsulation, specifically an idea best summed up by Item 23 of Meyer's "Effective C++":

Prefer non-member non-friend functions to member functions.

Ignoring the lack of a friend mechanism for a moment, are non-member functions considered preferable to member functions in Python, too?

An obligatory, asinine example:

class Vector(object):
    def __init__(self, dX, dY):
        self.dX = dX
        self.dY = dY

    def __str__(self):
        return "->(" + str(self.dX) + ", " + str(self.dY) + ")"

    def scale(self, scalar):
        self.dX *= scalar
        self.dY *= scalar

def scale(vector, scalar):
    vector.dX *= scalar
    vector.dY *= scalar

Given v = Vector(10, 20), we can now either call v.scale(2) or scale(v, 2) to double the magnitude of the vector.

Considering the fact that we're using properties in this case, which of the two options - if any - is better, and why?


Solution

  • Interesting question.

    You're starting from a different place than most questions coming from Java programmers, which tend to assume that you need classes when you mostly don't. Generally, in Python there's no point in having classes unless you're specifically doing data encapsulation.

    Of course, here in your example you are actually doing that, so the use of classes is justified. Personally, I'd say that since you do have a class, then the member function is the best way to go: you're specifically doing an operation on that particular vector instance, so it makes sense for the function to be a method on Vector.

    Where you might want to make it a standalone function (we don't really use the word "member" or "non-member") is if you need to make it work with multiple classes which don't necessarily inherit from each other or a common base. Thanks to duck-typing, it's fairly common practice to do this: specify that your function expects an object with a particular set of attributes or methods, and do something with those.