May 21, 2009

Factory Functions

In the last entry we discussed passing callables (using functions and classes) as arguments to functions, and calling them within the body of the function. This time we'll look at how functions can return more esoteric objects - again including functions and classes.

Beginners sometimes ask how a function can be made to return "more than one object". The strict answer is that it can't. The single object it returns can be a container, though, allowing several values to be extracted from the returned object. If you want a function to return three values the easiest way to arrange this is to have it return a three-element tuple, and then extract the individual values using an unpacking assignment. Here's a simple example.
def powers(a):
return a, a*a, a*a*a

a, square, cube = powers(10)
print(a, square, cube)
This prints 10 100 1000, showing that the three returned values have indeed been assigned to individual variables. Functions can return more complex objects than simple containers, though. A frequent example in the programming literature is a function that returns some newly-created function each time it is called. Typically the function returned will vary according to one or more of the arguments passed to the call that creates it.
def make_fun(power, debug=False):
def pow(x):
result = x ** power
if debug:
print("power(%s, %s) returned" % (x, power), result)
return result
return pow

squarer = make_fun(2)
cuber = make_fun(3, True)

print([f(9) for f in (squarer, cuber)])
A call to make_fun() results in a function being defined as make_fun's function body is being executed. This function (whose local name is pow) contains references to the arguments passed to make_fun, and is returned by the call to make_fun to be assigned and eventually called. The calls to the function are inside a list comprehension just because that's the easiest way to call a set of functions with the same argument(s).

The output from this is
power(9, 3) returned 729
[81, 729]
The debugging output from cuber is seen before the list comprehension because all calls have to return their values before the list comprehension is complete and ready for printing.

You may be familiar with the concept of a mixin class. Such classes are designed to take advantage of Python's multiple inheritance features to add functionality to any chosen classes, by creating a new class which is a subclass of both the mixin and the chosen class. You can see an exellent example of this in the socket library, where a ThreadingMixIn class is defined and can be used to extend the features of the basic UDPServer class like this:
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
Note that the newly-declared ThreadingUDPServer class doesn't specify any behavior of its own, it merely inherits - first from the mixin and then from the base class, meaning that methods defined in the mixin take precedence over those defined in the base server class. One problem with this, however, is that it doesn't allow for any variation in the mixin classes - by the time you use them they are already created, and it's too late.

In the same way that we can parameterize functions, however, we can parameterize classes as well. Suppose we want to provide a trivial mixin to print the class's name in either upper- or lower-case. Not very inspiring, but the simplest example I could think of to get the point over, so please bear with me if you can think of simpler ways to do this. One possibility is this.
def mixin(cls, lcase=True):
class Mixin:
def nprint(self):
if lcase:
print(str(cls).lower())
else:
print(str(cls).upper())
class Result(Mixin, cls):
pass
return Result

class FirstClass:
pass

Cl1 = mixin(FirstClass, True)
cl1 = Cl1()
cl1.nprint()

mixin(FirstClass, False)().nprint()

Here the function first defines a mixin class, then creates a new class from the mixin and the base class provided as an argument. The interpreter cares not at all where the class definitions come from - classes are first-class objects just like functions and strings, and can just as easily be passed as function arguments as obtained any other way.

The output from the program, which I am sure you are waiting for with bated breath, is
<class '__main__.firstclass'>
<CLASS '__MAIN__.FIRSTCLASS'>
which shows that the program runs, and that the mixin class's behavior is conditioned by the function's second argument.

Now you might choose to argue that this isn't a very natural example, and I'd be inclined to agree with you. All I have to say besides that is, you try coming up with these examples and see how you like it. If anyone chooses to contribute a more natural example that can be expressed without too much extra code I'll be happy to write about it.

3 comments:

Unknown said...

You might be interested in my factory module, an OO approach to factories: http://pypi.python.org/pypi/Factory/

EOL (Eric O LEBIGOT) said...

In the mixin() function, you could put the test on lcase outside of the nprint() definition:

if lcase:
class Mixin:…
else:
class Mixin:…

Thus, creating new Mixin instances is faster, as no test on lcase is performed. This may matter.

Steve said...

@EOL. That's a good point. If the function is called many times there'd be a definite small win.

Probably the biggest gain is the improvement in readability. Your solution puts the computation where (and when) it belongs, since the decision is properly made ance and for all when the factory function is called.