Case study: library class inheritance with property declarations

cdleary at gmail.com cdleary at gmail.com
Thu Aug 2 16:08:14 CEST 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