[Tutor] classes and names [__function__(arg)]
Danny Yoo
dyoo@hkn.eecs.berkeley.edu
Sun, 23 Dec 2001 13:36:08 -0800 (PST)
On Sat, 22 Dec 2001, Kevin McCormick wrote:
> Greetings all! I am writing a module of spreadsheet type date
> functions and I would like to do some things with the function names.
> Some of the functions are written really for supporting the actually
> useful functions and I don't quite understand the __function__(arg),
The '__function__(arg)' stuff are "magical" functions --- there are a
preset number of them, described here:
http://www.python.org/doc/current/ref/specialnames.html
The reason they're magical is because they get called when we do unusual
stuff. For example, let's say that we're trying to represent
Red/Green/Blue color triples in some program:
###
>>> class Color:
... def __init__(self, r, g, b):
... self.r, self.g, self.b = r, g, b
... def add(self, other):
... return Color(self.r + other.r,
... self.g + other.g,
... self.b + other.b)
...
>>> c1 = Color(0, 0, 0)
>>> c2 = Color(3, 4, 7)
>>> c3 = c1.add(c2)
>>> c3.r, c3.g, c3.b
(3, 4, 7)
###
It might be nice to be able to say something like:
###
>>> c3 = c1 + c2
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: __add__ nor __radd__ defined for these operands
###
but, as the error message suggests, Python doesn't know what it means to
'+' two things together... that is, until we define an __add__ method:
###
>>> class Color:
... def __init__(self, r, g, b):
... self.r, self.g, self.b = r, g, b
... def add(self, other):
... return Color(self.r + other.r,
... self.g + other.g,
... self.b + other.b)
... def __add__(self, other): return self.add(other)
...
>>> c1 = Color(1, 2, 3)
>>> c2 = Color(4, 5, 6)
>>> c3 = c1 + c2
>>> c3.r, c3.g, c3.b
(5, 7, 9)
###
And now it works! "__function__(arg)" functions are hooks to make things
like addition look more natural. Take a look at the documentation on the
link above, and you'll see a bunch of interesting stuff. (In the C++
language, we're "overloading operators".)
> __function(arg), and _function(arg) conventions.
The single underscore '_function(arg)', is meant to tell someone reading
the code to not fiddle around with it too much --- a '_function(arg)'
implies to a person that it's supposed to be for internal use only by the
class.
For example, if we were to write a Fraction class:
###
def gcd(a, b):
if b == 0: return a
return gcd(b, a % b)
class Fraction:
def __init__(self, n, d):
self.n, self.d = n, d
self._reduce()
def __add__(self, other):
return Fraction(self.n * other.d + other.n*self.d,
self.d * other.d)
def _reduce(self):
denom = gcd(self.n, self.d)
self.n = self.n / denom
self.d = self.d / denom
###
The single leading underscore in '_reduce' tells someone not to call
_reduce() explicitly; it's a small utility member of the Fraction class,
and we shouldn't have to call it ourselves when using a Fraction.
'__function(arg)" implies the same kind of protection, but doubly so: it
gets Python to mangle up the function names so that it becomes very hard
for the outside to directly call '__function(arg)'. To tell the truth,
I've never reallly felt the need to use this.
Hope this helps!