Is this considered black magic?
Skip Montanaro
montanaro at tttech.com
Mon Nov 12 01:47:10 EST 2001
Laura> I want to do something which is conceptually very simple.
Laura> Given a list of objects, call make everybody call the same
Laura> method you want to run. Sort of like apply(), but for
Laura> methods.
I'm used to thinking of a proxy that forwards attribute lookups to a
list of objects as a "collection". That's what LYMB called it:
http://www.crd.ge.com/esl/cgsp/fact_sheet/objorien/index.html
(that page is probably five years old - convert all verbs to past
tense...) and I suspect that's what Smalltalk calls it.
In LYMB, the Collection class was used quite heavily. I always
wondered why I never needed it in Python. Maybe I did but just didn't
know it. ;-) At any rate, here's a simple Collection class for Python
that does pretty much what you asked for. It implements get (for
retrieving attribute values) and call (for calling attributes)
methods. It uses the new 2.2 type/class stuff. You should be able to
run it under 2.1 by subclassing from UserList.UserList instead of
list.
Note that it's not an error to attempt to forward a call to an object
that doesn't implement it. The output simply doesn't include any
result, so unlike map(), the length of the collection and the output
produced by the get and call methods need not be the same. That
behavior can easily be changed in a couple different ways, either by
lopping off the hasattr check in the get method or providing a default
value in the get method to substitute for the missing result.
Implementation is left as an exercise for the reader. ;-)
--
Skip Montanaro (skip at pobox.com)
http://www.mojam.com/
http://www.musi-cal.com/
#!/usr/bin/env python
class Collection(list):
"""Collections group objects together and forward attribute lookups.
At least this is the Collection I remember from LYMB (an interpreted OO
language developed by Bill Lorensen et al at GE CRD), which almost
certainly got it from Smalltalk.
I don't understand why I've never needed it in Python. It was
indispensable in LYMB. (hmmm...)
This doesn't look a lot like LYMB's collection class, but then Python
doesn't look much like LYMB. It's also a helluva lot easier to write
in Python than LYMB because all LYMB's classes were written in C. ;-)
"""
def get(self, attr):
"""return a list of attributes from ourselves"""
return Collection([getattr(x, attr)
for x in self
if hasattr(x, attr)])
def call(self, attr, *args, **kwds):
"""return the result of calling 'attr' for each of our elements"""
attrs = self.get(attr)
return Collection([x(*args, **kwds)
for x in attrs
if callable(x)])
import time
class Food:
def __init__(self):
self.t = time.time()
time.sleep(0.5)
class Ham(Food):
def say(self):
print "I am Ham, and I don't want any arguments, thank you."
return ()
def speak_up(self, arg):
print 'I am Ham, and my arguments are %s' % arg
return arg
class Eggs(Food):
def speak_up(self, arg='bacon'):
print 'I am Eggs, and my arguments are %s' % arg
return arg
class Spam(Food):
def shout(self, arg1, arg2='sausage'):
print 'I AM SPAM AND MY ARGUMENTS ARE %s AND %s' % (arg1, arg2)
return (arg1, arg2)
def main():
c = Collection()
# kind of boring example...
c.extend(range(3))
print c.get("__hash__")
print c.call("__hash__")
print
# slightly more interesting example...
c = Collection([Ham(), Eggs(), Spam()])
print "*** when was this food made? ***"
times = c.get("t")
print map(round, times, [2]*len(times))
print
print "*** what kind of food is it? ***"
classcol = c.get("__class__")
print classcol.get("__name__")
print
print "*** shouting ***"
print c.call("shout", "nickel", "dime")
print
print "*** speaking up ***"
print c.call("speak_up", "juice please")
print
print "*** saying ***"
print c.call("say")
print
if __name__ == "__main__":
main()
More information about the Python-list
mailing list