How can we write a class factory that dynamically redefines the __init__ function etc.

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Tue May 6 01:14:48 EDT 2008


En Mon, 05 May 2008 20:34:32 -0300, John Schroeder <jschroed at gmail.com>  
escribió:

> Basically I have some classes like this:
>
>
> ###############################################################################
> # 0x01: ModeCommand
> ###############################################################################
> class ModeCommand:
>     """This is the Mode Command Packet class."""
>     def __init__(self, mode, command, id=0x01):
>         """The unspecial init function."""
>         self.mode = mode
>         self.command = command
>         self.id = id
>
>     def RawData(self):
>         return [self.id, self.mode, self.command]
>
>     def __getitem__(self, index):
>         """[] operator (read): indexes from the byte data."""
>         return self.RawData()[index]
>
>     def __str__(self):
>         """Print a nice thing."""
>         string = "Mode = %d\n" % self.mode + \
>                  "Command = %d\n" % self.command + \
>                  "ID = %d\n\n" % self.id
>         return string
>
> ###############################################################################
> # 0x02: CallRequest
> ###############################################################################
> class CallRequest:
>     """This is the Call Request Packet class.  (Look familiar?)"""
>     def __init__(self, service_number, id=0x02):
>         """The unspecial init function."""
>         self.service_number = service_number
>         self.id = id
>
>     def RawData(self):
>         return [self.id, self.service_number]
>
>     def __getitem__(self, index):
>         """[] operator (read): indexes from the byte data."""
>         return self.RawData()[index]
>
>     def __str__(self):
>         """Print a nice thing."""
>         string = "Service Number = %d\n" % self.service_number + \
>                  "ID = %d\n\n" % self.id
>         return string
>
>
> ###############################################################################
> # Test Code
> ###############################################################################
> x = ModeCommand(mode=1, command=0)
> print x[:]
> print x
> y = CallRequest(service_number=2001)
> print y[:]
> print y
>
>
> Which is ok but I had to do this over 100 times.  It is difficult to
> maintain and no one else can understand what the heck I did here.  So I  
> was
> trying to look a metaclasses to do something fancy like this:

You don't need a metaclass, nor a class factory. You only need a base  
class.
__getitem__ is the same on both classes, and presumably on all: move it to  
the base class.
And the only difference between both versions of __init__, RawData and  
__str__ is the name and order of the attributes: let's make them a  
parameter, a class attribute. The default id attribute may be a class  
attribute too.
So in principle, if you have the right base class, the subclasses could be  
defined as simply as:

class ModeCommand(Base):
     """This is the Mode Command Packet class."""

     parameters = "mode command id".split()
     id = 0x01

class CallRequest(Base):
     """This is the Call Request Packet class.  (Look familiar?)"""

     parameters = "service_number id".split()
     id = 0x02

and that's all. Now we have to write the Base class:

class Base(object):
     parameters = None # redefined in all subclasses
     id = None

     def __init__(self, **kw):
         assert self.parameters is not None # must be redefined
         for name in kw:
             if name in self.parameters:
                 setattr(self, name, kw[name])
             else:
                 raise NameError, "unknown parameter: %s" % name
         assert self.id is not None # must be redefined

     def RawData(self):
         return [getattr(self, name) for name in self.parameters]

     def __getitem__(self, index):
         return self.RawData()[index]

     def __str__(self):
         return '\n'.join(
                      ["%s = %r" % (name, getattr(self, name))
                       for name in self.parameters])

py> x = ModeCommand(mode=1, command=0)
py> print x[:]
[1, 0, 1]
py> print x
mode = 1
command = 0
id = 1
py> y = CallRequest(service_number=2001, id=13)
py> print y[:]
[2001, 13]
py> print y
service_number = 2001
id = 13

(the convention is to use lowercase names for attributes: rawdata instead  
of RawData)

-- 
Gabriel Genellina




More information about the Python-list mailing list