Re: [Python-Dev] [1.6]: UserList, Dict: Do we need a UserString class?

Hi!
I had something like this in my mind: class MutableString(UserString): """Python strings are immutable objects. But of course this can be changed in a derived class implementing the missing methods. >>> s = MutableString() >>> s[0:5] = "HUH?" """ def __setitem__(self, char): .... def __setslice__(self, i, j, substring): ....
I was obviously too tired and too eager to get this out! Thanks for reviewing and responding so quickly. I will add them. Regards, Peter -- Peter Funk, Oldenburger Str.86, D-27777 Ganderkesee, Germany, Fax:+49 4222950260 office: +49 421 20419-0 (ArtCom GmbH, Grazer Str.8, D-28359 Bremen)

On Wed, 29 Mar 2000, Peter Funk wrote:
Then add the default in the constructor for MutableString.... eagerly-waiting-for-UserString.py-ly y'rs, Z. -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com

Hi! Moshe Zadka:
eagerly-waiting-for-UserString.py-ly y'rs, Z.
Well, I've added the missing methods. Unfortunately I ran out of time now and a 'test_userstring.py' derived from 'src/Lib/test/test_string.py' is still missing. Regards, Peter ---- 8< ---- 8< ---- cut here ---- 8< ---- schnipp ---- 8< ---- schnapp ---- #!/usr/bin/env python """A user-defined wrapper around string objects Note: string objects have grown methods in Python 1.6 This module requires Python 1.6 or later. """ from types import StringType, UnicodeType import sys class UserString: def __init__(self, string): self.data = string def __str__(self): return str(self.data) def __repr__(self): return repr(self.data) def __int__(self): return int(self.data) def __long__(self): return long(self.data) def __float__(self): return float(self.data) def __hash__(self): return hash(self.data) def __cmp__(self, string): if isinstance(string, UserString): return cmp(self.data, string.data) else: return cmp(self.data, string) def __contains__(self, char): return char in self.data def __len__(self): return len(self.data) def __getitem__(self, index): return self.__class__(self.data[index]) def __getslice__(self, start, end): start = max(start, 0); end = max(end, 0) return self.__class__(self.data[start:end]) def __add__(self, other): if isinstance(other, UserString): return self.__class__(self.data + other.data) elif isinstance(other, StringType) or isinstance(other, UnicodeType): return self.__class__(self.data + other) else: return self.__class__(self.data + str(other)) def __radd__(self, other): if isinstance(other, StringType) or isinstance(other, UnicodeType): return self.__class__(other + self.data) else: return self.__class__(str(other) + self.data) def __mul__(self, n): return self.__class__(self.data*n) __rmul__ = __mul__ # the following methods are defined in alphabetical order: def capitalize(self): return self.__class__(self.data.capitalize()) def center(self, width): return self.__class__(self.data.center(width)) def count(self, sub, start=0, end=sys.maxint): return self.data.count(sub, start, end) def encode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.encode(encoding, errors)) else: return self.__class__(self.data.encode(encoding)) else: return self.__class__(self.data.encode()) def endswith(self, suffix, start=0, end=sys.maxint): return self.data.endswith(suffix, start, end) def find(self, sub, start=0, end=sys.maxint): return self.data.find(sub, start, end) def index(self, sub, start=0, end=sys.maxint): return self.data.index(sub, start, end) def isdecimal(self): return self.data.isdecimal() def isdigit(self): return self.data.isdigit() def islower(self): return self.data.islower() def isnumeric(self): return self.data.isnumeric() def isspace(self): return self.data.isspace() def istitle(self): return self.data.istitle() def isupper(self): return self.data.isupper() def join(self, seq): return self.data.join(seq) def ljust(self, width): return self.__class__(self.data.ljust(width)) def lower(self): return self.__class__(self.data.lower()) def lstrip(self): return self.__class__(self.data.lstrip()) def replace(self, old, new, maxsplit=-1): return self.__class__(self.data.replace(old, new, maxsplit)) def rfind(self, sub, start=0, end=sys.maxint): return self.data.rfind(sub, start, end) def rindex(self, sub, start=0, end=sys.maxint): return self.data.rindex(sub, start, end) def rjust(self, width): return self.__class__(self.data.rjust(width)) def rstrip(self): return self.__class__(self.data.rstrip()) def split(self, sep=None, maxsplit=-1): return self.data.split(sep, maxsplit) def splitlines(self, maxsplit=-1): return self.data.splitlines(maxsplit) def startswith(self, prefix, start=0, end=sys.maxint): return self.data.startswith(prefix, start, end) def strip(self): return self.__class__(self.data.strip()) def swapcase(self): return self.__class__(self.data.swapcase()) def title(self): return self.__class__(self.data.title()) def translate(self, table, deletechars=""): return self.__class__(self.data.translate(table, deletechars)) def upper(self): return self.__class__(self.data.upper()) class MutableString(UserString): """mutable string objects Python strings are immutable objects. This has the advantage, that strings may be used as dictionary keys. If this property isn't needed and you insist on changing string values in place instead, you may cheat and use MutableString. But the purpose of this class is an educational one: to prevent people from inventing their own mutable string class derived from UserString and than forget thereby to remove (override) the __hash__ method inherited from ^UserString. This would lead to errors that would be very hard to track down. A faster and better solution is to rewrite the program using lists.""" def __init__(self, string=""): self.data = string def __hash__(self): raise TypeError, "unhashable type (it is mutable)" def __setitem__(self, index, sub): if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + sub + self.data[index+1:] def __delitem__(self, index): if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + self.data[index+1:] def __setslice__(self, start, end, sub): start = max(start, 0); end = max(end, 0) if isinstance(sub, UserString): self.data = self.data[:start]+sub.data+self.data[end:] elif isinstance(sub, StringType) or isinstance(sub, UnicodeType): self.data = self.data[:start]+sub+self.data[end:] else: self.data = self.data[:start]+str(sub)+self.data[end:] def __delslice__(self, start, end): start = max(start, 0); end = max(end, 0) self.data = self.data[:start] + self.data[end:] def immutable(self): return UserString(self.data) def _test(): s = UserString("abc") u = UserString(u"efg") # XXX add some real tests here? return 0 if __name__ == "__main__": sys.exit(_test())

On Wed, 29 Mar 2000, Peter Funk wrote:
Great work, Peter! I really like UserString. However, I have two issues with MutableString: 1. I tshouldn't share implementation with UserString, otherwise your algorithm are not behaving with correct big-O properties. It should probably use a char-array (from the array module) as the internal representation. 2. It shouldn't share interface iwth UserString, since it doesn't have a proper implementation with __hash__. All in all, I probably disagree with making MutableString a subclass of UserString. If I have time later today, I'm hoping to be able to make my own MutableString

Hi!
Moshe Zadka schrieb:
Hmm.... I don't understand what you mean with 'big-O properties'. The internal representation of any object should be considered ... umm ... internal.
2. It shouldn't share interface iwth UserString, since it doesn't have a proper implementation with __hash__.
What's wrong with my implementation of __hash__ raising a TypeError with the attribution 'unhashable object'. This is the same behaviour, if you try to add some other mutable object as key to dictionary:
As I tried to point out in the docstring of 'MutableString', I don't want people actually start using the 'MutableString' class. My Intentation was to prevent people from trying to invent their own and than probably wrong MutableString class derived from UserString. Only Newbies will really ever need mutable strings in Python (see FAQ). May be my 'MutableString' idea belongs somewhere into the to be written src/Doc/libuserstring.tex. But since Newbies tend to ignore docs ... Sigh. Regards, Peter -- Peter Funk, Oldenburger Str.86, D-27777 Ganderkesee, Germany, Fax:+49 4222950260 office: +49 421 20419-0 (ArtCom GmbH, Grazer Str.8, D-28359 Bremen)

On Wed, 29 Mar 2000, Peter Funk wrote:
Yes, but s[0] = 'a' Should take O(1) time, not O(len(s))
A subtype shouldn't change contracts of its supertypes. hash() was implicitly contracted as "raising no exceptions". -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com

Let's not confuse subtypes and subclasses. One of the things implicit in the discussion on types-sig is that not every subclass is a subtype! Yes, this violates something we all learned from C++ -- but it's a great insight. No time to explain it more, but for me, Peter's subclassing UserString for MutableString to borrow implementation is fine. --Guido van Rossum (home page: http://www.python.org/~guido/)

On Wed, 29 Mar 2000, Guido van Rossum wrote:
Oh, I agree with this. An earlier argument which got snipped in the discussion is why it's a bad idea to borrow implementation (a totally different argument) -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com

On Wed, 29 Mar 2000, Peter Funk wrote:
Then add the default in the constructor for MutableString.... eagerly-waiting-for-UserString.py-ly y'rs, Z. -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com

Hi! Moshe Zadka:
eagerly-waiting-for-UserString.py-ly y'rs, Z.
Well, I've added the missing methods. Unfortunately I ran out of time now and a 'test_userstring.py' derived from 'src/Lib/test/test_string.py' is still missing. Regards, Peter ---- 8< ---- 8< ---- cut here ---- 8< ---- schnipp ---- 8< ---- schnapp ---- #!/usr/bin/env python """A user-defined wrapper around string objects Note: string objects have grown methods in Python 1.6 This module requires Python 1.6 or later. """ from types import StringType, UnicodeType import sys class UserString: def __init__(self, string): self.data = string def __str__(self): return str(self.data) def __repr__(self): return repr(self.data) def __int__(self): return int(self.data) def __long__(self): return long(self.data) def __float__(self): return float(self.data) def __hash__(self): return hash(self.data) def __cmp__(self, string): if isinstance(string, UserString): return cmp(self.data, string.data) else: return cmp(self.data, string) def __contains__(self, char): return char in self.data def __len__(self): return len(self.data) def __getitem__(self, index): return self.__class__(self.data[index]) def __getslice__(self, start, end): start = max(start, 0); end = max(end, 0) return self.__class__(self.data[start:end]) def __add__(self, other): if isinstance(other, UserString): return self.__class__(self.data + other.data) elif isinstance(other, StringType) or isinstance(other, UnicodeType): return self.__class__(self.data + other) else: return self.__class__(self.data + str(other)) def __radd__(self, other): if isinstance(other, StringType) or isinstance(other, UnicodeType): return self.__class__(other + self.data) else: return self.__class__(str(other) + self.data) def __mul__(self, n): return self.__class__(self.data*n) __rmul__ = __mul__ # the following methods are defined in alphabetical order: def capitalize(self): return self.__class__(self.data.capitalize()) def center(self, width): return self.__class__(self.data.center(width)) def count(self, sub, start=0, end=sys.maxint): return self.data.count(sub, start, end) def encode(self, encoding=None, errors=None): # XXX improve this? if encoding: if errors: return self.__class__(self.data.encode(encoding, errors)) else: return self.__class__(self.data.encode(encoding)) else: return self.__class__(self.data.encode()) def endswith(self, suffix, start=0, end=sys.maxint): return self.data.endswith(suffix, start, end) def find(self, sub, start=0, end=sys.maxint): return self.data.find(sub, start, end) def index(self, sub, start=0, end=sys.maxint): return self.data.index(sub, start, end) def isdecimal(self): return self.data.isdecimal() def isdigit(self): return self.data.isdigit() def islower(self): return self.data.islower() def isnumeric(self): return self.data.isnumeric() def isspace(self): return self.data.isspace() def istitle(self): return self.data.istitle() def isupper(self): return self.data.isupper() def join(self, seq): return self.data.join(seq) def ljust(self, width): return self.__class__(self.data.ljust(width)) def lower(self): return self.__class__(self.data.lower()) def lstrip(self): return self.__class__(self.data.lstrip()) def replace(self, old, new, maxsplit=-1): return self.__class__(self.data.replace(old, new, maxsplit)) def rfind(self, sub, start=0, end=sys.maxint): return self.data.rfind(sub, start, end) def rindex(self, sub, start=0, end=sys.maxint): return self.data.rindex(sub, start, end) def rjust(self, width): return self.__class__(self.data.rjust(width)) def rstrip(self): return self.__class__(self.data.rstrip()) def split(self, sep=None, maxsplit=-1): return self.data.split(sep, maxsplit) def splitlines(self, maxsplit=-1): return self.data.splitlines(maxsplit) def startswith(self, prefix, start=0, end=sys.maxint): return self.data.startswith(prefix, start, end) def strip(self): return self.__class__(self.data.strip()) def swapcase(self): return self.__class__(self.data.swapcase()) def title(self): return self.__class__(self.data.title()) def translate(self, table, deletechars=""): return self.__class__(self.data.translate(table, deletechars)) def upper(self): return self.__class__(self.data.upper()) class MutableString(UserString): """mutable string objects Python strings are immutable objects. This has the advantage, that strings may be used as dictionary keys. If this property isn't needed and you insist on changing string values in place instead, you may cheat and use MutableString. But the purpose of this class is an educational one: to prevent people from inventing their own mutable string class derived from UserString and than forget thereby to remove (override) the __hash__ method inherited from ^UserString. This would lead to errors that would be very hard to track down. A faster and better solution is to rewrite the program using lists.""" def __init__(self, string=""): self.data = string def __hash__(self): raise TypeError, "unhashable type (it is mutable)" def __setitem__(self, index, sub): if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + sub + self.data[index+1:] def __delitem__(self, index): if index < 0 or index >= len(self.data): raise IndexError self.data = self.data[:index] + self.data[index+1:] def __setslice__(self, start, end, sub): start = max(start, 0); end = max(end, 0) if isinstance(sub, UserString): self.data = self.data[:start]+sub.data+self.data[end:] elif isinstance(sub, StringType) or isinstance(sub, UnicodeType): self.data = self.data[:start]+sub+self.data[end:] else: self.data = self.data[:start]+str(sub)+self.data[end:] def __delslice__(self, start, end): start = max(start, 0); end = max(end, 0) self.data = self.data[:start] + self.data[end:] def immutable(self): return UserString(self.data) def _test(): s = UserString("abc") u = UserString(u"efg") # XXX add some real tests here? return 0 if __name__ == "__main__": sys.exit(_test())

On Wed, 29 Mar 2000, Peter Funk wrote:
Great work, Peter! I really like UserString. However, I have two issues with MutableString: 1. I tshouldn't share implementation with UserString, otherwise your algorithm are not behaving with correct big-O properties. It should probably use a char-array (from the array module) as the internal representation. 2. It shouldn't share interface iwth UserString, since it doesn't have a proper implementation with __hash__. All in all, I probably disagree with making MutableString a subclass of UserString. If I have time later today, I'm hoping to be able to make my own MutableString

Hi!
Moshe Zadka schrieb:
Hmm.... I don't understand what you mean with 'big-O properties'. The internal representation of any object should be considered ... umm ... internal.
2. It shouldn't share interface iwth UserString, since it doesn't have a proper implementation with __hash__.
What's wrong with my implementation of __hash__ raising a TypeError with the attribution 'unhashable object'. This is the same behaviour, if you try to add some other mutable object as key to dictionary:
As I tried to point out in the docstring of 'MutableString', I don't want people actually start using the 'MutableString' class. My Intentation was to prevent people from trying to invent their own and than probably wrong MutableString class derived from UserString. Only Newbies will really ever need mutable strings in Python (see FAQ). May be my 'MutableString' idea belongs somewhere into the to be written src/Doc/libuserstring.tex. But since Newbies tend to ignore docs ... Sigh. Regards, Peter -- Peter Funk, Oldenburger Str.86, D-27777 Ganderkesee, Germany, Fax:+49 4222950260 office: +49 421 20419-0 (ArtCom GmbH, Grazer Str.8, D-28359 Bremen)

On Wed, 29 Mar 2000, Peter Funk wrote:
Yes, but s[0] = 'a' Should take O(1) time, not O(len(s))
A subtype shouldn't change contracts of its supertypes. hash() was implicitly contracted as "raising no exceptions". -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com

Let's not confuse subtypes and subclasses. One of the things implicit in the discussion on types-sig is that not every subclass is a subtype! Yes, this violates something we all learned from C++ -- but it's a great insight. No time to explain it more, but for me, Peter's subclassing UserString for MutableString to borrow implementation is fine. --Guido van Rossum (home page: http://www.python.org/~guido/)

On Wed, 29 Mar 2000, Guido van Rossum wrote:
Oh, I agree with this. An earlier argument which got snipped in the discussion is why it's a bad idea to borrow implementation (a totally different argument) -- Moshe Zadka <mzadka@geocities.com>. http://www.oreilly.com/news/prescod_0300.html http://www.linux.org.il -- we put the penguin in .com
participants (3)
-
Guido van Rossum
-
Moshe Zadka
-
pf@artcom-gmbh.de