Using class attributes

Jean-Michel Pichavant jeanmichel at sequans.com
Tue Feb 16 16:00:28 CET 2010


Arnaud Delobelle wrote:
> 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
>
>   
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



More information about the Python-list mailing list