This should be one of the simplest ways, the field you mention is the internal data (inherited from the UserString class):
from collections import UserString
class LowerStr(UserString):
def __init__(self, value):
super().__init__(value)
self.data = self.data.lower()
s1 = LowerStr("ABC")
print("s1: {}".format(s1))
print("s1 class: {}".format(s1.__class__))
print("s1 class mro: {}".format(s1.__class__.mro()))
Output:
s1: abc
s1 class: <class '__main__.LowerStr'>
s1 class mro: [<class '__main__.LowerStr'>, <class 'collections.UserString'>, <class 'collections.abc.Sequence'>, <class 'collections.abc.Reversible'>, <class 'collections.abc.Collection'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Iterable'>, <class 'collections.abc.Container'>, <class 'object'>]
This gives you all the methods from str and you can customize them at will.
If you prefer to subclass, so there is no internal data attribute:
class InsensitiveStr(str):
def __new__(cls, value):
if isinstance(value, str):
return super().__new__(cls, value.lower())
else:
raise ValueError
s1 = InsensitiveStr("ABC")
print(s1)
print(type(s1))
print(s1.__class__)
print(s1.__class__.__mro__)
Note the differences in mro.
Outputs
abc
<class '__main__.InsensitiveStr'>
<class '__main__.InsensitiveStr'>
(<class '__main__.InsensitiveStr'>, <class 'str'>, <class 'object'>)