views:

83

answers:

2

I've read some tutorials on Python metaclasses. I've never used one before, but I need one for something relatively simple and all the tutorials seem geared towards much more complex use cases. I basically want to create a template class that has some pre-specified body, but takes its base class as a parameter. Since I got the idea from C++/D templates, here's an example of what the code I want to write would look like in C++:

template<class T>
    class Foo : T {
        void fun() {}
    }
+3  A: 

A fairly straight-forward translation should do it:

def template(baseclass):
    class Foo(baseclass):
        def fun(self):
            print "%s::fun()" % self.__class__.__name__

    # derive a class name for the subclass returned (optional)
    Foo.__name__ = "_".join([Foo.__name__, baseclass.__name__])
    return Foo

class Base1:
    def bar(self):
        print "Base1::bar()"

class Base2:
    def bar(self):
        print "Base2::bar()"

Foo_Base1 = template(Base1)
print 'Foo_Base1 base classes:', Foo_Base1.__bases__

Foo_Base2 = template(Base2)
print 'Foo_Base2 base classes:', Foo_Base2.__bases__

d1 = Foo_Base1()
d1.fun()
d1.bar()

d2 = Foo_Base2()
d2.fun()
d2.bar()

# Ouput:

# Foo_Base1 base classes: (<class __main__.Base1 at 0x00A56C70>,)
# Foo_Base2 base classes: (<class __main__.Base2 at 0x00ABFA78>,)
# Foo_Base1::fun()
# Base1::bar()
# Foo_Base2::fun()
# Base2::bar()
# Foo_Base2::fun()
# Base2::bar()

Explanation: In the code the (poorly-named) template() function is an example of what is commonly called a "class factory". For more information (warning: shameless plug) see my answer to the question "What exactly is a Class Factory?" on this site.

Edit: Added code to create different class name for each subclass returned (inspired by @AaronMcSmooth's answer with insightful comment about potential confusion when debugging).

martineau
Awesome, I guess metaclasses are overkill for something so simple. This solution makes tons of sense in hindsight, since types are first class objects in Python, and classes can be created at runtime. I guess I haven't gotten comfortable enough with the dynamic language way of thinking to come up w/ it on my own yet, though.
dsimcha
Templates *are* metaclasses in C++, a strongly-typed language. In a weakly-typed one like Python, where classes are also objects as you noted, it's often not necessary to go there -- but when you do, you're not limited to template arguments and can do some amazing things.
martineau
@martineau: **Python is strongly-typed**, you can't add a string and an integer in Python like you'd do in a weakly-typed language like Javascript. Incidentally, **Python is also dynamically-typed**.
Lie Ryan
@Lie Ryan: You're quite right -- I used the wrong terms and got strong/weak confused with static/dynamic typing. Thanks for the correction.
martineau
A: 

This is meaningless in Python, since it does not have templates. My understanding of parameterized templates in C++ (which is rather vague, since it is many years since I have looked at them), is that it acts like a class factory, and can create a subclass of whatever class you give it that has additional methods or attributes added.

In Python you can do this with a factory function that takes a class and returns a new class at runtime:

In [1]: def subclassFactory(cls):
   ...:     class Foo(cls):
   ...:         def fun(self):
   ...:             return "this is fun"
   ...:     return Foo
   ...: 

In [2]: class A(object):
   ...:     pass
   ...: 

In [5]: C = subclassFactory(A)

In [6]: C
Out[6]: <class '__main__.Foo'>
In [7]: c = C()
In [9]: c.fun()
Out[9]: 'this is fun'
In [10]: isinstance(c, A)
Out[10]: True
Dave Kirby