Arnaud Delobelle wrote:
Leo Breebaart <l...@lspace.org> writes:

Chris Rebert <c...@rebertia.com> writes:

On Mon, Feb 15, 2010 at 10:29 AM, Leo Breebaart <l...@lspace.org> wrote:

I have a base class Foo with a number of derived classes FooA,
FooB, FooC, etc. Each of these derived classes needs to read
(upon initialisation) text from an associated template file
FooA.tmpl, FooB.tmpl, FooC.tmpl, etc.
[...]
But, since this information is the same for every instance of
each derived class, I was wondering if there was a way to achieve
the same thing outside of the __init__ function, and just have
these assignments be done as a class attribute (i.e. so that I
can refer to FooA.template_body, etc.)
Metaclasses to the rescue!:

class WithTemplateAttrs(type):
    def __new__(cls, name, bases, dct):
        klass =3D type.__new__(cls, name, bases, dct)
        klass.template_filename =3D "%s.tmpl" % name
        klass.template_body =3D read_body_from(klass.template_filename)
        return klass

class Foo(object):
    __metaclass__ =3D WithTemplateAttrs
    #rest of class body here

Now just have FooA, FooB, etc. subclass Foo as before. They'll
automatically get the attributes generated.
Thanks for the feedback! I am thrilled that an actual real-life
issue I'm having may be resolvable by metaclasses (which so far
I've only admired from afar but never considered relevant to my
day-to-day work), but unfortunately I'm still struggling to get
this to work.

If I add your code, what happens is that the Foo class will try
to read "Foo.tmpl", which does not exist -- it is only the
derived classes FooA etc, that need to execute this code, not Foo
itself.

And actually that makes sense -- I think my problem was not too
clearly thought out to begin with. Of course it's not possible to
associate a single template_body with the Foo class, it will be a
different template for each derived class. So at best I need to
associate your metaclass with each derived class, but at that
point I might as well just read the template in the __init__()
method with __class__.__name__, and use lazy evaluation / caching
to avoid doing the actual file-reading work more than once.

I think.

Descriptors to the rescue :)

def read_body_from(filename):
    print "** loading content **"
    return "<content of '%s'>" % filename

# This is a kind of class property
class TemplateFilename(object):
    def __get__(self, obj, cls):
        return "%s.tmpl" % cls.__name__

# And this is a kind of cached class property
class TemplateBody(object):
    def __get__(self, obj, cls):
        try:
            return cls._body
        except AttributeError:
            cls._body = read_body_from(cls.template_filename)
            return cls._body

class Foo(object):
    template_filename = TemplateFilename()
    template_body = TemplateBody()

class FooA(Foo):
   pass

class FooB(Foo):
   pass

# In action:
FooA.template_filename
'FooA.tmpl'
FooB.template_filename
'FooB.tmpl'
FooA.template_body
** loading content **
"<content of 'FooA.tmpl'>"
FooA.template_body
"<content of 'FooA.tmpl'>"
foob = FooB()
foob.template_filename
'FooB.tmpl'
foob.template_body
** loading content **
"<content of 'FooB.tmpl'>"
foob.template_body
"<content of 'FooB.tmpl'>"

HTH

While all these proposals are giving interesting technical anwsers to the OP problem, I think that the OP original code is still the best (_imo_).

 class Foo(object):
     def __init__(self):
         self.template_filename = "%s.tmpl" % self.__class__.__name__
         self.template_body = read_body_from(self.template_filename)


That is true the same attributes are defined for every instance, but, honestly, who cares ? (unless you target the best class design 2010 price :-) )
This solution beat the others in terms of simplicity and readability.

It's still usefull to train your mind with decorators and metaclasses though, they can save your life eventually.

JM
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to