(You top-posted, which confuses the sequence of message text. So I
clipped it off and posted my message at the bottom, which is the
convention on this newsgroup)
Vincent Davis wrote:
DaveA posted
import random, functools
class Person:
def __init__(self, size):
self.size = size
def __str__(self):
return "Person of size %s" % self.size
class MakePeople:
def __init__(self, random_func):
self.random_func = random_func
def make_them(self, count):
return [Person(self.random_func()) for i in xrange(count)]
people_maker = MakePeople(functools.partial(random.gauss, 100, 2))
persons = people_maker.make_them(100)
for person in persons:
print person.size
I changed the last line, from
print person
print person.size
So this does what I want, but I am not sure why.
I read the entry about functools.partial but it was not very clear to me.
If I
people_maker = MakePeople(random.gauss(100, 2))
then I only get 1 random #.
and if I
MakePeople('random.gauss(100, 2)')
then I just a fix string
So DaveA uses
functools.partial(random.gauss, 100, 2)
not obvious to me from that it should not be
functools.partial(random.gauss(100, 2))
and I guess the other key is
Person(self.random_func())
Also now this
people_maker = MakePeople(123)
does not work, which is not terrible.
Anyone have some more to add, I would not have confidence in applying this
to new situations and it seems.
Also I thank DaveA improving my Python conventions. I am really bad about
that. Is there a cheat sheet for Python conventions.
Like class (Capitals), def (two_words), I guess I should make my own.
Thanks
Vincent Davis
720-301-3003
<snip>
For the official Python style guide, see
http://www.python.org/dev/peps/pep-0008/
There are a couple of things going on in my code sample, and I'll try to
elaborate on them. I'm not claiming it's the 'right' answer, just that
it satisfies what I think were the most important goals you had. But if
you want to save the list in the MakePeople() instance, you'd add an
additional parameter to its constructor, and combine the second method
into __init__(). But as someone else points out, at that point, it's
then hardly worth making a class out of it.
First the tough part. In your original code, your caller was doing:
listofp = makepeople(random.guass(100, 2))
But that passes only a single value into the makepeople constructor. If you're
always going to use gaussian, then you can just move the function call into the
make_them() loop. But if you want to do the same thing, but with a different
distribution, you need to pass a function object instead. Learning about
function objects is very useful. You should really play with them in a simpler
situation, to get an idea how they work.
At its simplest, a function object is just the function name, without
parentheses. You can store it (and pass it as a parameter to another function)
just like any other object. And then when you actually want to call it, you
can use the new name with parentheses, kind of like an alias to the original
function name.
import math
def indirection(funcobject, argument):
return funcobject(math.pi/180 * argument)
print indirection(math.sin, 30)
print indirection(math.cos, 30)
Now what happens here? The indirection() function calls an entirely
different function the two times it's called, one time it calls sin, and
the other time it calls cos. As long as all the arguments to the
function are going to be supplied here, there's no confusion. Try the
same thing with any other set of functions, given that all of them take
the same number and types of arguments.
This opens the door to all sorts of things, such as a mini-calculator
(following is mostly pseudo-code):
funcname = getnamefrom user()
angleinradians = getnumberfromuser()
converter_dict = { "sin": math.sin, "cos":math.cos }
print converter_dict[funcname](angleinradians)
So the values of the dictionary are actual function objects, ready to be
called.
What happens if some of the function parameters are known to the caller,
and not to the callee? I'll use the random.gauss example again. If we
want the caller to be able to specify them, we could do something like
the following:
def print_indirection(funcobject, arg1, arg2, count):
for i in xrange(count):
print funcobject(arg1, arg2)
and call it:
print_indirection(random.gauss, 100, 2, 44)
to get 44 values with a gaussian distributon. But now suppose we wanted
to be able to use the same function with random.getrandbits() ? That
function only takes a single parameter, so the print funcobject() would
blow up.
functools.partial lets us bind a function object with some or all of
its arguments already attached. So in our case, the caller (who knows
what the arguments look like) gets to lay them out, without being stuck
with any particular ones.
So, we rewrite print_indirection something like this:
def print_indirection(funcobject, count):
for i in xrange(count):
print funcobject()
and call it like:
print_indirection(functools.partial(random.gauss, 100, 2), 44)
print_indirection(functools.partial(random.getrandbits, 17), 44)
The key here is that
functools.partial(random.gauss, 100, 2)
is a function object that takes no parameters, because the 2 parameters
of the underlying function have already been bound.
Now this can be generalized a bit, but I hope you understand it better
than before. You have to play with it. Make lists or dicts of your own
functions, with or without binding parameters. The key is that such a
list should have uniform signatures, which in the examples I've shown
means none of the function objects take any (more) arguments.
DaveA
_______________________________________________
Tutor maillist - Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor