Case study: library class inheritance with property declarations
cdleary at gmail.com
cdleary at gmail.com
Thu Aug 2 10:08:14 EDT 2007
On Aug 2, 6:49 am, cdle... at gmail.com wrote:
> Hi all,
>
> It's possible that I'm missing the obvious -- I've been up for over 24
> hours and I'm most likely dehydrated from mass coffee intake, but I
> figure many people in similar circumstances will be searching
> comp.lang.python one day, so here goes!
>
> class LibraryClass(object):
>
> """
> A class whose implementation is out of my hands.
> """
>
> def __init__(self):
> """
> Follows good dynamic-language form and binds all instance
> variables at initialization time.
> """
> # Omitted: complex initialization functionality.
> self.useful_attr = None
>
> class MyInheritedClass(LibraryClass):
>
> """
> My refinement of the functionality offered by the LibraryClass. I
> now want the instance to initialize with a reference to an
> external
> object, and the useful_attr defined in the superclass will now
> reference an attribute of that external object via fget.
>
> Changing the attribute of the external object has undefined
> behavior, so I want to omit an fset in the property declaration;
> however, I have to provide some way for the superclass to
> initialize useful_attr -- I can't change the superclass' code, as
> it
> resides in a library that is out of my hands.
> """
>
> def __init__(self, external_obj):
> LibraryClass.__init__(self)
> self._external_obj = external_obj
>
> def get_useful_attr(self):
> return self._external_obj.proxy_useful_attr
>
> useful_attr = property(fget=get_useful_attr)
>
> def test():
> class _Fake(object):
> pass
> external_obj = _Fake()
> external_obj.proxy_useful_attr = 12
> spam = MyInheritedClass(external_obj)
>
> if __name__ == '__main__':
> test()
> EOF
>
> If you're one of those people who doesn't like laboriously reading
> contrived examples (elitists ;) I'll boil it down for you: Library
> class initializes some attribute, but derived class wants to eliminate
> fsets for said attribute. As a result, our ideal solution
>
> Of course, this means that the derived class will raise an error in
> some circumstances where the base class wouldn't (when you're setting
> that attribute), but one can assume that the inheritance is
> worthwhile.
>
> How do I come up with silly solutions to circumvent this? Let me count
> the ways...
>
> 1. So-and-so: make an fset that does nothing. This ignores (what
> should be) errors in code that uses MyInheritedClass in an attempt to
> accommodate a useless statement in the base class -- surely non-ideal.
> 2. The ugly one: since you can probably view the library, copy and
> paste the complex initialization functionality in the above, but leave
> out the bad statement. This not only forfeits the ideals of
> inheritance, but makes you totally incompatible with future library
> changes.
> 3. Cheerleader: Pure evil. On top of the ugliness of 2, you assume
> that across library revisions the indenting won't change and that the
> troublesome statement will remain on the same line, and pull off one
> of these babies:
>
> def super_evil_test():
> from inspect import getsourcelines
> exec(''.join([line[4:] for line in
> getsourcelines(LibraryClass.__init__)[0][:-1]]))
> LibraryClass.__init__ = __init__
> test() # Passes, but several angels no longer get their wings
>
> Totally kidding, everybody! I hope Guido doesn't read this thread...
>
> And this concludes the sleep deprived rambling that follows the
> somewhat interesting case in point. Thoughts?
I'm sorry -- the solution was not /enough/ coffee. Got another cup and
sat down with the type/class unification doc, and found this thought-
stimulating portion:
http://www.python.org/download/releases/2.2/descrintro/#property
If you want to override the __get__ operation for properties when used
as a class attribute, you can subclass property - it is a new-style
type itself - to extend its __get__ method, or you can define a
descriptor type from scratch by creating a new-style class that
defines __get__, __set__ and __delete__ methods.
...
The get method won't be called when the property is accessed as a
class attribute (C.x) instead of as an instance attribute (C().x).
Seeing as how property is just a wrapper class, we don't need to
declare it in the class body, though it /is/ the convention and the
way it's done in all the docs I've seen. We fix our inherited class to
be the following:
[snip]
class MyInheritedClass(LibraryClass):
"""
My refinement of the functionality offered by the LibraryClass. I
now want the instance to initialize with a reference to an
external
object, and the useful_attr defined in the superclass will now
reference an attribute of that external object via fget.
Changing the attribute of the external object has undefined
behavior, so I want to omit an fset in the property declaration;
however, I have to provide some way for the superclass to
initialize useful_attr -- I can't change the superclass' code, as
it
resides in a library that is out of my hands.
"""
def __init__(self, external_obj):
LibraryClass.__init__(self)
self.useful_attr = property(fget=self.get_useful_attr)
self._external_obj = external_obj
def get_useful_attr(self):
return self._external_obj.proxy_useful_attr
[snip]
And it tests like a charm.
More information about the Python-list
mailing list