High Order Messages in Python

Michael ms at cerenity.org
Sun Oct 23 12:00:06 EDT 2005


eduardo.padoan at gmail.com wrote:

> I'm reading about "high order messages" in Ruby by Nat Pryce, and
> thinking if it could be  util and if so, if it could be done in Python.

Nice sunday afternoon exercise. Yes, you can do this in python. This is
based on a relatively naive translation of the ruby version:

class HigherOrderMessage(object):
    def __init__(self,handler):
      self.handler = handler

class Do(HigherOrderMessage):
    def __HOM__(self, methname, *args):
        "implement ruby's method_missing idea"
        try:
            for e in self.handler:
                meth = getattr(e, methname)
                meth(*args)
        except TypeError: # Handle non-iterator, could be nicer
             if self.handler is not None:
                 meth = getattr(self.handler, methname)
                 meth(*args)
    def __getattribute__(self, methname):
        try:
            return super(Do,self).__getattribute__(methname)
        except AttributeError:
            def myHom(*args):
                return self.__HOM__(methname, *args)
            return myHom

class Where(HigherOrderMessage):
    def __HOM__(self, methname, *args):
        "implement ruby's method_missing idea"
        try:
            r = List()
            for e in self.handler:
                meth = getattr(e, methname)
                if meth(*args):
                    r.append(e)
            return r
        except TypeError:
             r = List()
             if self.handler is not None:
                 meth = getattr(self.handler, methname)
                 if meth(*args):
                     r.append(self.handler)
             return r
    #
    def __getattribute__(self, methname):
        "Probably belongs in the baseclass"
        try:
            return super(Where,self).__getattribute__(methname)
        except AttributeError:
            def myHom(*args):
                return self.__HOM__(methname, *args)
            return myHom

class enumerable(object):
    def do(self):    return Do(self)
    def where(self): return Where(self)

class List(enumerable,list):
    "List using enumerable as a mixin"

class Claimant(enumerable):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        self.benefits = 0

    def retired(self):
        return (self.gender == "male" and self.age >= 65) or \
               (self.gender == "female" and self.age >= 60)

    def receive_benefit(self,amount):
        self.benefits = self.benefits + amount

    def __str__(self):
        return "%s,%s age: %s benefits: %s" % (
                  self.name,
                  self.gender,
                  str(self.age),
                  str(self.benefits)
               )

#
# Create an enumerable list capable of responding to 
#

claimants = List([  # Just a list which is enumerable as well
    Claimant("tom", 32, "male"),
    Claimant("dick", 64, "male"),
    Claimant("harry", 128, "male"),
    Claimant("Ivanova", 32, "female"),
    Claimant("Kochansky", 64, "female"),
    Claimant("Sung", 128, "female"),
])

# First the normal python way I prefer this)
for claimant in claimants:
    if claimant.retired():
        claimant.receive_benefit(50)

# Display the results
for claimant in claimants: print str(claimant)
print

# The more direct translation of :
# claimants.select {|e| e.retired?}.each {|e| e.receive_benefit 50}

[ e.receive_benefit(50) for e in claimants if e.retired() ]

# Display the results
for claimant in claimants: print str(claimant)
print

# The single claimant version of the higher order message approach
# re-uses the last claimant from above. This one unconditionally
# grants benefits.
#
claimant.do().receive_benefit(50)
print claimant
print

# Iterating over a bunch of Claimants with the higher order message
# approach. This conditionally updates the claimaints

claimants.where().retired().do().receive_benefit(50)
# display results
for claimant in claimants: print str(claimant)

I'm not convinced I'd actually *use* this approach(*), but it does show that
you can certainly take this approach in python. It's certainly interesting
though.
   (*) Largely because there's a bunch of magic happening as far as the user
       (next programmer to edit the file) is concerned. After all you can't
       find the definition of retried in "where" or "claimant"; you can't
       find the definition of "receive_benefit" in "do", "retired", "where"
       or "claimants". 

I'm also pretty sure there's other things you'd want to do more than the
above generally speaking if you wanted to handle inheritance/etc nicely.
(That does make me wonder as well if you'd need to do more in ruby as
well)

However, this certainly isn't a case of "ruby can do this, and python
can't", because clearly python CAN do it :-)

Regards,


Michael




More information about the Python-list mailing list