
On Wed, 21 Dec 2022 at 17:03, Steven D'Aprano <steve@pearwood.info> wrote:
The status quo mostly hurts *lightweight* subclasses:
class TurkishString(str): def upper(self): return TurkishString(str.upper(self.replace('i', 'İ'))) def lower(self): return TurkishString(str.lower(self.replace('I', 'ı')))
That's fine so long as the *only* operations you do to a TurkishString is upper or lower. As soon as you do concatenation, substring replacement, stripping, joining, etc you get a regular string.
Also not a great example, honestly. Part of the problem is that there *are no good examples*. You need something that subclasses a core data type, does not change its constructor in any way, and needs to always get back another of itself when any method is called. But in every other way, it is its superclass. I think defaultdict comes close, but it changes the constructor's signature; StrEnum and IntEnum come very close, but apparently they're just special cases and can be written off as irrelevant; there really aren't that many situations where this even comes up. Part of the problem is that it's really not clear which methods should return "the same type" and which should return a core data type. Clearly __len__ on a string should return a vanilla integer, regardless of the precise class of string; but should bit_length on an integer return an int of the subclass, or a plain integer? Is it different just because it happens to return the same data type on a vanilla int? What about as_integer_ratio() - should that return a tuple of two of the same type, or should it return (self, 1) ? Remember that you're asking the int type to make these decisions globally. ChrisA