Case study: library class inheritance with property declarations

Steve Holden steve at holdenweb.com
Thu Aug 2 11:14:53 EDT 2007


cdleary at gmail.com wrote:
> On Aug 2, 7:08 am, cdle... at gmail.com wrote:
>> 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.
> 
> Last post -- I swear.
> 
> I failed to realize that it's all part of an extremely well defined
> attribute resolution protocol, and handled via the descriptor
> specification. Discussing descriptors was on the TODO list for the
> type/class unification document, but there's a very fulfilling
> explanation by Raymond Hettinger: http://users.rcn.com/python/download/Descriptor.htm
> Also, the official doc is here: http://docs.python.org/ref/descriptors.html
> 
> Maybe the documentation for the property builtin should make reference
> to the descriptor specification? If nobody thinks this is silly, I'll
> submit a documentation patch in a few days.
> 
> Sorry for the spam -- hope someone besides me learns from it!
> 
And I hope you learn to get more sleep ;-)

short-on-sleep-myself-this-week-ly y'rs  - steve
-- 
Steve Holden        +1 571 484 6266   +1 800 494 3119
Holden Web LLC/Ltd           http://www.holdenweb.com
Skype: holdenweb      http://del.icio.us/steve.holden
--------------- Asciimercial ------------------
Get on the web: Blog, lens and tag the Internet
Many services currently offer free registration
----------- Thank You for Reading -------------




More information about the Python-list mailing list