[Tutor] best practices for where to set instance member variables
Steven D'Aprano
steve at pearwood.info
Sat Sep 4 03:41:58 CEST 2010
On Sat, 4 Sep 2010 05:17:34 am Gregory, Matthew wrote:
> Hi all,
>
> Is there a guideline on where instance member variables should be set
> within a class?
The normal terminology used in Python is "attributes" instead of "member
variables", and "methods" rather than "member functions".
[...]
> class Foo:
> def __init__(self, a):
> self._f1(a)
> def _f1(self, a):
> self.a = a
In general, I would avoid that style of coding. Method _f1 operates by
side-effect. That makes it harder to test, which means you probably
avoid testing it, which means it probably has bugs. The more
complicated _f1 is, the more you need to test it, and the less likely
you are to do so. This is a bad mix!
You should try to limit methods with side-effects to only those that
*need* to have side-effects, like list.append() and similar.
(Note: functional programming proponents would argue that you *never*
need to have side-effects, except perhaps in one tiny little corner of
the language where you handle file I/O. Maybe so, but Python is not and
never will be a pure FP language, and there's no need to take it to
such extremes to realise that side-effects are often bad and should be
avoided as much as possible.)
> -or-
>
> class Foo:
> def __init__(self, a):
> self.a = self._f1(a)
> def _f1(self, a):
> return a
That's much better. Now you can easily write a test to ensure that _f1
is correct, without having to worry about side-effects.
However, remember that _f1 is being called from within the
initialization method. That means that at the point that _f1 is called,
self is not fully initialized! That makes it hard to reason about the
state of the instance, and hard to know which attributes exist, and
which methods are safe to call. These added complications should be
avoided. Since you can't rely on self, it's best to avoid passing it as
an argument to the method:
@staticmethod # nothing like C++ static methods
def _f1(a):
# Method _f1 can't see self or self's class.
return a
@classmethod
def _f1(cls, a):
# Method _f1 can't see self but does see self's class.
return a
Instead of hoping that the caller will remember that _f1 can't rely on
self being fully initialized, you can prohibit it.
The worst case is when _f1 needs access to self. That requires careful
handling, and is best avoided.
Aside:
The use of private methods in Python is looked on with mild suspicion.
It's not so much frowned upon, as considered a sign that the author is
trying to write Java or C++ instead of Python. My own feeling is,
anytime I think I want a private method, that's possibly a hint that
the method is too specific and not general (i.e. useful) enough. But
I'll assume that you have a good reason for making _f1 a private method
rather than public. Just remembering that in Python, there's no such
thing as "private", it's just a naming convention.
> The first method seems to me to 'hide' where the variables get set,
Which is another reason to avoid it.
> but in looking through some of my site-packages, developers seem to
> do this both ways.
Yes, they do. Doesn't mean you have to follow their bad ideas :)
--
Steven D'Aprano
More information about the Tutor
mailing list