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 -- Arnaud -- http://mail.python.org/mailman/listinfo/python-list