Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Type subclassing: bug or feature

0 views
Skip to first unread message

Aahz

unread,
Jun 13, 2002, 4:08:40 PM6/13/02
to
Consider the following code:

class MyStr(str):
def contains(self, value):
return self.find(value) >= 0

s = MyStr("hello, world!")
s = s.capitalize()
if s.contains('Hello'):
print "Found it!"

It fails with an AttributeError when it calls s.contains(), because
s.capitalize() returned a str instead of a MyStr. Anyone want to take a
whack at defending this as the correct behavior?
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

"I had lots of reasonable theories about children myself, until I
had some." --Michael Rios

Bengt Richter

unread,
Jun 13, 2002, 4:44:35 PM6/13/02
to
On 13 Jun 2002 16:08:40 -0400, aa...@pythoncraft.com (Aahz) wrote:

>Consider the following code:
>
>class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
>s = MyStr("hello, world!")
>s = s.capitalize()
>if s.contains('Hello'):
> print "Found it!"
>
>It fails with an AttributeError when it calls s.contains(), because
>s.capitalize() returned a str instead of a MyStr. Anyone want to take a
>whack at defending this as the correct behavior?

I don't think you could generally coerce base class method results
to subclass instances, so I think it has to be explicit. Maybe it
could be done with a method list for a more compact way (using a metaclass?)
Anyway:

>>> class MyStr(str):
... def contains(self, value):
... return self.find(value) >= 0
... def capitalize(self):
... return MyStr(str.capitalize(self))
...


>>> s = MyStr("hello, world!")
>>> s = s.capitalize()
>>> if s.contains('Hello'):

... print "Found it!"
...
Found it!


Regards,
Bengt Richter

Bjorn Pettersen

unread,
Jun 13, 2002, 5:06:01 PM6/13/02
to
> From: Aahz [mailto:aa...@pythoncraft.com]
>
> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone
> want to take a whack at defending this as the correct behavior?

Well, in e.g. C++ this would make perfect sense, since a base class
method can't know the real type of self/this. In Python we can do better
of course:

>>> class A:
... def foo(self):
... return self.__class__()
...
>>> class B(A):
... pass
...
>>> b = B()
>>> b.foo()
<__main__.B instance at 0x00864CC0>

which version is correct is mostly a religious question, but personally
I think that if we _can_ do better than C++ we should <wink>.

File a bug and see what Guido says...

-- bjorn


David LeBlanc

unread,
Jun 13, 2002, 5:14:49 PM6/13/02
to
> -----Original Message-----
> From: python-l...@python.org
> [mailto:python-l...@python.org]On Behalf Of Aahz
> Sent: Thursday, June 13, 2002 13:09
> To: pytho...@python.org
> Subject: Type subclassing: bug or feature
>
>
> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?
> --
> Aahz (aa...@pythoncraft.com) <*>
> http://www.pythoncraft.com/

Looks like a bug to me. How can self be something else then what it is?
Should MySocket return a socket, thus stripping off any additional
functionality added by my derived class?

David LeBlanc
Seattle, WA USA

Chris Liechti

unread,
Jun 13, 2002, 5:24:09 PM6/13/02
to
aa...@pythoncraft.com (Aahz) wrote in news:aeau48$111$1...@panix1.panix.com:

> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?

solving this at the C level would require patching many stingobject factory
functions... e.g. PyString_FromStringAndSize for capitalize(). but it would
indeed be desireable.

i think it would like to have a even more genral mechanism. i would like to
have a possibility to replace the builtin type. e.g. i could replace the
builtin "str" and all strings from now on would be instaces of my class.
but that's clearly impossible in the current implementation.

<hacker warning>
like Bengt already sugested just automated ;-) ...

>>> class mstr(str):
... for name,m in str.__dict__.items():
... if type(m) == type(str.find):
... exec """def %s(*args, **kwargs):
... q = str.%s(*args, **kwargs)
... if type(q) is str: return mstr(q)
... else: return q\n""" % (name, name)
... def __contains__(self, other):
... return str.find(self, other) >= 0
...
>>> s = mstr("hello")
>>> type(s.capitalize())
<class '__main__.mstr'>
>>> 'll' in s.capitalize()
1
</hacker warning>

chris

--
Chris <clie...@gmx.net>

Gustavo Cordova

unread,
Jun 13, 2002, 5:18:15 PM6/13/02
to
> >
> > Consider the following code:
> >
> > class MyStr(str):
> > def contains(self, value):
> > return self.find(value) >= 0
> >
> > s = MyStr("hello, world!")
> > s = s.capitalize()
> > if s.contains('Hello'):
> > print "Found it!"
> >
> > It fails with an AttributeError when it calls s.contains(), because
> > s.capitalize() returned a str instead of a MyStr. Anyone
> want to take a
> > whack at defending this as the correct behavior?
> > --
> > Aahz (aa...@pythoncraft.com) <*>
> > http://www.pythoncraft.com/
>
> Looks like a bug to me. How can self be something else then
> what it is?
> Should MySocket return a socket, thus stripping off any additional
> functionality added by my derived class?
>
> David LeBlanc
> Seattle, WA USA
>

Hmmm...

maybe all methods of builtin types should do:

return self.__class__(return_value)

when returning a new object (like str methods).

-gustavo


holger krekel

unread,
Jun 13, 2002, 5:28:11 PM6/13/02
to
Aahz wrote:
> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?

class Names(str):
def __init__(self, namelist):
str.__init__(self, ":".join(namelist))

what would you expect

Names([1,2,3]).capitalize()

to return? An exception? I think it's dangerous to assume anything
about the subtyped classes' constructor. You *do* need a constructor
because strings are immutable so 'str.capitalize' can't modify
anything in place.

if-you-subtype-cake-you-gotta-eat-it-all-ly y'rs, holger


holger krekel

unread,
Jun 13, 2002, 5:50:28 PM6/13/02
to
To Aahz wrote:

> Aahz wrote:
> > Consider the following code:
> >
> > class MyStr(str):
> > def contains(self, value):
> > return self.find(value) >= 0
> >
> > s = MyStr("hello, world!")
> > s = s.capitalize()
> > if s.contains('Hello'):
> > print "Found it!"
> >
> > It fails with an AttributeError when it calls s.contains(), because
> > s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> > whack at defending this as the correct behavior?
>
> class Names(str):
> def __init__(self, namelist):
> str.__init__(self, ":".join(namelist))
>
> what would you expect
>
> Names([1,2,3]).capitalize()

oops. sorry. should have checked it better (or at all<self.wink>)...

class Names(str):
def __new__(cls, namelist):
return str.__new__(str, ":".join(namelist))

n=Names(['tim','peter','guido'])

But hopefully my point was understandable that it is hard
to guess a return type for 'n.capitalize()' now ...

> to return? An exception? I think it's dangerous to assume anything
> about the subtyped classes' constructor. You *do* need a constructor
> because strings are immutable so 'str.capitalize' can't modify
> anything in place.
>
> if-you-subtype-cake-you-gotta-eat-it-all-ly y'rs, holger

cheers,

holger


Grant Griffin

unread,
Jun 13, 2002, 6:34:22 PM6/13/02
to
In article <aeau48$111$1...@panix1.panix.com>, aa...@pythoncraft.com says...

>
>Consider the following code:
>
>class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
>s = MyStr("hello, world!")
>s = s.capitalize()
>if s.contains('Hello'):
> print "Found it!"
>
>It fails with an AttributeError when it calls s.contains(), because
>s.capitalize() returned a str instead of a MyStr. Anyone want to take a
>whack at defending this as the correct behavior?

Sounds like a bug to me, but perhaps fixing it at some point was part of the
plan from the beginning.

Editorially speaking, although I hadn't thought of this problem specificially, I
guess it might be another reason to use subclassing to augment behavior, not
change it.

I haven't figured out whether this sort of thing could happen with other, more
usefully subclassed types like list and dict, but I guess it could occur
whenever a built-in type returns its own (er, "parent") type. I bet somebody
can think of an example.

straight-man-ly y'rs,

=g2

_________________________________________________________________________

Grant R. Griffin g...@dspguru.com
Publisher of dspGuru http://www.dspguru.com
Iowegian International Corporation http://www.iowegian.com

Bengt Richter

unread,
Jun 13, 2002, 9:58:14 PM6/13/02
to

Interesting way to go if you can't know except dynamically at run time
what types base class methods will return.

I don't know how you'd fully generalize this process though, and it
seems a shame to have to wrap everything in an extra layer. It would
be nice to be able to specify coercions in the subclass in a way that
base class methods could access quickly to check whether to call a coercion
routine on a result, e.g.,

__coercions__ = (str, mstr), (bool, No_Yes) # just as example with several coercions

Also str.split could even access this, and produce a list of MyStr substrings.
And also deeper bases' methods could check on self.__coercions__

Just OTTOMH, so not very thought through ;-)

BTW, I just thought of a 'where' keyword for 'for' e.g., the above for would become

for name,m in str.__dict__.items() where type(m) == type(str.find):

sort of like sql select ... where ... or the list comprehension if.

Regards,
Bengt Richter

Gerhard Häring

unread,
Jun 13, 2002, 10:09:24 PM6/13/02
to
* Aahz <aa...@pythoncraft.com> [2002-06-13 16:08 -0400]:

> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?

I once had the same problem with subclassing the list type/class: you'd
need to override every single method of the base class to make
subclassing of builtin Python types really useful. I ended up just
implementing the parts of the sequence protocol that I needed in the
particular case.

I'll look at the other answers if there is some clever soluiton using
__getattr__ or a metaclass. One day I'll have to figure out that
metaclass stuff, anyways. I still have no idea what it's all about.

Gerhard
--
This sig powered by Python!
Außentemperatur in München: 17.1 °C Wind: 1.2 m/s


Gerhard Häring

unread,
Jun 13, 2002, 10:12:16 PM6/13/02
to
* Gustavo Cordova <gcor...@hebmex.com> [2002-06-13 16:18 -0500]:

> maybe all methods of builtin types should do:
>
> return self.__class__(return_value)
>
> when returning a new object (like str methods).

Looks like a waste of performance in about 99 % of the time. Hopefully,
this can be optimized at the C level.

Gerhard
--
This sig powered by Python!

Außentemperatur in München: 17.1 °C Wind: 1.3 m/s


Tim Peters

unread,
Jun 13, 2002, 10:09:16 PM6/13/02
to
[Aahz]

> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?

Short course:

http://www.python.org/sf/468887

Long course:

http://www.python.org/sf/460020

Making sure this pisses you off in all cases required one of the biggest
patch sets that went into Python 2.2 <wink>.

Emile van Sebille

unread,
Jun 13, 2002, 10:56:28 PM6/13/02
to
Bengt Richter

> BTW, I just thought of a 'where' keyword for 'for' e.g., the above for
would become
>
> for name,m in str.__dict__.items() where type(m) ==
type(str.find):
>
> sort of like sql select ... where ... or the list comprehension if.
>

why not use if, as in

[ name,m for name,m in str.__dict__.items() if type(m) ==
type(str.find) ]

--

Emile van Sebille
em...@fenx.com

---------

Gustavo Cordova

unread,
Jun 14, 2002, 12:00:46 PM6/14/02
to
>
> * Gustavo Cordova <gcor...@hebmex.com> [2002-06-13 16:18 -0500]:
> > maybe all methods of builtin types should do:
> >
> > return self.__class__(return_value)
> >
> > when returning a new object (like str methods).
>
> Looks like a waste of performance in about 99 % of the time.
> Hopefully,
> this can be optimized at the C level.
>
> Gerhard
>

Well, it *was* supposed to be pseudo-code, I'm sure
that at the C level it can be done in a much more
efficient way, without having to resort to Python code.

:-P

:-)

-gustavo

pd: TGIF!!


Anders J. Munch

unread,
Jun 17, 2002, 7:14:06 AM6/17/02
to
"Aahz" <aa...@pythoncraft.com> wrote:
> Consider the following code:
>
> class MyStr(str):
> def contains(self, value):
> return self.find(value) >= 0
>
> s = MyStr("hello, world!")
> s = s.capitalize()
> if s.contains('Hello'):
> print "Found it!"
>
> It fails with an AttributeError when it calls s.contains(), because
> s.capitalize() returned a str instead of a MyStr. Anyone want to take a
> whack at defending this as the correct behavior?

Inheriting from a class/type with value semantics is usually bad
design. Use containment or helper functions instead.

it-so-happens-that-Python-has-support-for-creating-thusly-flawed-designs-
but-that-doesn't-make-it-a-good-idea-ly y'rs, Anders


0 new messages