Standing Objects ... possible?

Jason Orendorff jason at jorendorff.com
Mon Feb 11 04:28:53 EST 2002


Paul Rubin wrote:
> Jason Orendorff wrote:
> > Philip Swartzleonard writes:
> > > What i want is a construct like:
> > > 
> > > object Foo( ancestor ):
> > >     	pass
> > 
> > Yep, it exists.
> 
> This has gone past me somehow.  Can someone explain it?  Thanks.

I will, but I warn you that this isn't understood by many people.
You must use this knowledge only for good.  :)

When you write:

  class X(BasicX, OtherBaseClass):
      name = 'foo'
      bar = 2
      blah = None

what happens as a result is a lot like this:

  X = type('X',
           (BasicX, OtherBaseClass),
           {'name': 'foo', 'bar': 2, 'blah': None})

type is itself a builtin type; when Python calls it like that,
it creates a "type object", which is the same as saying that
it creates the class X.  The arguments, you can see, are
determined by the source code for the class.  They are:

  'X' - the class name
  (BasicX, OtherBaseClass) - a tuple of the base classes
  {...} - a dictionary of the new class's members.

If you define methods within class X, then those functions
go in the member dictionary too.

Here, try it yourself:

>>> X = type('MyType', (), {'n': 5})   # Create a class manually!
>>> X
<class '__main__.MyType'>
>>> X.n
5

Weird, huh?

"Metaclassing" means asking Python to call something else,
instead of type(), to create the class.  There are a few ways
to trigger this.  The easiest is to use __metaclass__.

def make_a_type(name, bases, dict):
    """ My custom function for creating a class. """
    # Do some silly things...
    print "Making a type!"
    print "A message for you:", dict.get('msg')
    dict['made_by'] = 'Jason'  # add my name here
    return type(name, bases, dict)  # type() does the real work

class X:
    __metaclass__ = make_a_type
    msg = 'Hello, world!'

>>> X.made_by
'Jason'

You can use whatever callable __metaclass__ you want.
For example, your __metaclass__ could be an actual class whose
constructor takes three arguments.

class Course(object):
    # This is actually a real, ordinary class.
    # Since we want to use it as a metaclass, its
    # constructor must take 3 arguments (plus 'self').
    def __init__(self, name, bases, dict):
        self.name = name
        self.bases = bases
        self.dict = dict
    def __repr__(self):
        return "<%s>" % self.name

class CS1280:
    __metaclass__ = Course
    professor = 'Cleese'
    title = 'Introduction to Programming with SPAMTRAN'

class CS6196:
    __metaclass__ = Course
    professor = 'van Rossum'
    title = 'Metaprogramming in Python 2.2'

Now you see that this is creating "standing objects" as desired.
Python creates CS1280 and CS6196 by calling Course(); so they
are Course objects.

(You could make the class Course a subclass of type,
if you really want the objects to behave like typical classes
rather than typical objects.)

(There is also a subtler way of getting a metaclass other than
type(); but I won't get into it.  For a more technical overview,
consult http://www.python.org/2.2/descrintro.html#metaclasses .)

## Jason Orendorff    http://www.jorendorff.com/




More information about the Python-list mailing list