Code design for a sub-class of built-ins

Code design for a sub-class of built-ins

Post by Steven D'A » Thu, 06 Jul 2006 01:11:47


I'm having problems with sub-classes of built-in types.

Here is a contrived example of my subclass. It isn't supposed
to be practical, useful code, but it illustrates my problem.

class MyStr(str):
"""Just like ordinary strings, except it exhibits special behaviour
for one particular value.
"""
magic = "surprise"
def __init__(self, value):
str.__init__(self, value)
def __len__(self):
if self == self.magic:
print "Nobody expects the Spanish Inquisition!"
return str.__len__(self)
def upper(self):
if self == self.magic:
print "Nobody expects the Spanish Inquisition!"
return str.upper(self)
# and so on for every last string method...


The obvious problem is, I have to create a custom method for every string
method -- and if strings gain any new methods in some future version of
Python, my subclass won't exhibit the correct behaviour.

Here's a slightly different example:

class MyInt(int):
"""Like an int but with special behaviour."""
def __init__(self, value, extra):
int.__init__(self, value=None)
self.extra = extra
def __add__(self, other):
if self.extra is None:
return int.__add__(self, other)
else:
return int.__add__(self, other) + self.extra
# and again with all the other methods

This one doesn't even work!

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: int() can't convert non-string with explicit base

Looks like my __init__ isn't even being called here. Why not, and how do I
fix this?

Is there a way to subclass built-in types without needing to write the
same bit of code for each and every method?

Am I approaching this the wrong way? Is there a better design I could be
using?

Thanks,



--
Steven.
 
 
 

Code design for a sub-class of built-ins

Post by Bruno Dest » Thu, 06 Jul 2006 02:26:36


You don't need to override __init__ here.



My my my....


which makes subclassing almost useless - except to pass isinstance() tests.


You could replace subclassing by composition/delegation using the
__getattr__ hook, but this would break tests on type/class :(

Or you could keep subclassing and use the __getattribute__ hook, but
this is more tricky and IIRC may have negative impact on lookup perfs.

Or you could use a metaclass to decorate (appropriate) str methods with
a decorator doing the additional stuff.

choose your poison !-)


Won't work. You need to override the __new__ staticmethod. Look for
appropriate doc here:
http://www.yqcomputer.com/ #__new__
(FWIW, read the whole page)

(snip)


cf above


cf above


probably !-)

HTH
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ' XXXX@XXXXX.COM '.split('@')])"

 
 
 

Code design for a sub-class of built-ins

Post by Dennis Lee » Thu, 06 Jul 2006 03:25:14

On Wed, 05 Jul 2006 02:11:47 +1000, Steven D'Aprano
< XXXX@XXXXX.COM > declaimed the following in
comp.lang.python:



Pardon... You are trying, if I understand this, to create an
integer with an initial value of None -- and None is not an integer
value.

I suspect what you really want (I'm not going to open an interpreter
to test) is:

def __init__(self, value, extra=None):
int.__init__(self, value)
--
Wulfraed Dennis Lee Bieber KD6MOG
XXXX@XXXXX.COM XXXX@XXXXX.COM
http://www.yqcomputer.com/
(Bestiaria Support Staff: XXXX@XXXXX.COM )
http://www.yqcomputer.com/
 
 
 

Code design for a sub-class of built-ins

Post by alea » Thu, 06 Jul 2006 08:41:38


...

As others already suggested, automating such decoration is pretty easy;
you can do it with either a custom metaclass or a simple post-processing
of your class in a loop. Untested details below, but the general idea
would be something like:

class SDAstr(str):
magic = 'whatever'

def _SDAdecorate(methodname, strmethod):
def decorated(self, *a, **k):
if self == self.magic: print "whatever!"
return strmethod(self, *a, **k)
setattr(SDAstr, methodname, decorated)

for methodname, strmethod in \
inspect.getmembers(str, inspect.ismethoddescriptor):
_SDAdecorate(methodname, strmethod)

and variants thereof (to provide more accurate metainformation with the
decorated methods -- name, docstring, signature, whatever; and/or to
encapsulate things in a custom metaclass; whatever).


...

Override __new__, which is what invariably gets called (before __init__,
which only gets called if __new__ returns an instance of the class that
you're instantiating).


Alex
 
 
 

Code design for a sub-class of built-ins

Post by Steven D'A » Thu, 06 Jul 2006 15:00:05


Perhaps not, but in a more realistic example I might need to.



Exactly. Don't think I don't know this is a horrible strategy -- that's
why I'm asking for ideas for a better way to do it *wink*



Yep.



Interesting... That will give me something to experiment with.


Thanks,


--
Steven.
 
 
 

Code design for a sub-class of built-ins

Post by Steven D'A » Thu, 06 Jul 2006 15:01:18


Yes, that's exactly what I meant -- it was a copy-and-paste and I didn't
clean it up correctly.

Thanks,


--
Steven.
 
 
 

Code design for a sub-class of built-ins

Post by Bruno Dest » Thu, 06 Jul 2006 18:41:47


Perhaps, but I can only comment on the example you gave !-)
Also, *you* are aware of this, but please keep in mind that cl.py is
read by a lot of newbies.

(snip)


AFAICT, the last solution is probably the better of the three (well, at
least it's the better I can think of actually).

For the record, care to give more informations about your real use case?

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ' XXXX@XXXXX.COM '.split('@')])"
 
 
 

Code design for a sub-class of built-ins

Post by Steven D'A » Thu, 06 Jul 2006 21:32:19


That's great, thanks. You've given me an angle of attack to take and see
where it leads.


--
Steven.
 
 
 

Code design for a sub-class of built-ins

Post by Steven D'A » Thu, 06 Jul 2006 21:48:15


[snip]


Equal parts a learning exercise and a simple example of a genetic
algorithm.

I felt that the natural way to approach this would be for an object to
mutate itself, rather than have an external function that operated on a
string and returned a new string. The obvious approach was to subclass str
and give it methods to modify itself in place, but of course strings are
immutable.

This got me wondering how hard it would be to create a mutable string
class, whether I should look at subclassing list (to get the mutability)
and then add string-like methods to it, or try something completely
different.

A little bit of experimentation soon had me realising that I didn't
understand Python's new-style class model well enough to do these things
properly, hence the learning exercise.


Thanks for everybody's help on this.



--
Steven.