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