Using class attributes
Arnaud Delobelle
arnodel at googlemail.com
Tue Feb 16 09:38:51 EST 2010
Leo Breebaart <leo at lspace.org> writes:
> Chris Rebert <clp2 at rebertia.com> writes:
>
>> On Mon, Feb 15, 2010 at 10:29 AM, Leo Breebaart <leo at 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
More information about the Python-list
mailing list