Welcome, guest | Sign In | My Account | Store | Cart

Do not use this function - instead use the built in type(name, bases, dict) constructor This is a function that builds a subclass of some base classes. I wrote it while unaware that type could be used as a constructor.

Python, 27 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys as _sys

def subclass(bases, name, suite={}, doc=None):
    """Return a class that inherits from bases

    bases is the base class or classes, name is the name of the class, suite is a mapping of 
    attributes that the subclass should have.

    Unfortunately only the metaclass of the first base class will be used at class construction.  
    Suggestions on how to fix that are welcome. 
    """ 
    assert bases, "At least one base class must be supplied"
    if not hasattr(bases, '__iter__'):
        bases = (bases,)
    class cls(bases[0]):
        # We can't use the *bases form prior to Python 3.  If the later bases do anything clever in
        # thier metaclasses, we won't recieve their effects.  :-(
        locals().update(suite)
        # This seems to work reliably in CPython, it's not clear if that's standard
    cls.__name__ = name
    cls.__bases__ = bases
    # Stolen from the NamedTuple implementation
    if hasattr(_sys, '_getframe') and _sys.platform != 'cli':
        cls.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
    if doc:
        cls.__doc__ = doc
    return cls

When reading the implementation of named tuples I thought it was rather a shame that the implementation generated textual source code, and evaled it to yield a new class. Since the obvious means of doing this don't work (type.__new__ can't be called without an already existing subclass), this seems like a fairly clean alternative. It isn't however completely foolproof: Only the metaclass of the first base class will be used.

4 comments

Jonas Haag 15 years, 1 month ago  # | flag

len(bases) is not that good - use

if not hasattr(bases, '__iter__'):
    bases = (bases,)

or

if not isinstance(bases, (list, tuple)):
    bases = (bases,)

instead.

Peter Russell (author) 15 years, 1 month ago  # | flag

Jonas: Thanks for that. I've updated the recipe

Christophe: I don't understand what you're pointing out.

Peter Russell (author) 15 years, 1 month ago  # | flag

Christophe: Oh wait, yes I do! I had no idea type has a three argument form. That's brilliant! Thank you very much!

Created by Peter Russell on Sun, 22 Mar 2009 (MIT)
Python recipes (4591)
Peter Russell's recipes (1)

Required Modules

Other Information and Tasks