Python's preinit method is __new__
:
You can't set attributes on a frozen dataclass, so I use a namedtuple here.
from collections import namedtuple
_WithString = namedtuple("WithString", ["stringattr"])
class WithString:
def __new__(cls, input_string):
return _WithString(input_string.lower())
string_1 = WithString("TEST")
string_2 = WithString("test")
print("instances:", string_1, string_2)
print("attributes:", string_1.stringattr, string_2.stringattr)
print("compare instances:", string_1 == string_2)
print("compare attributes:", string_1.stringattr == string_2.stringattr)
Output:
instances: WithString(stringattr='test') WithString(stringattr='test')
compare instances: True
You can do this in __init__
too:
Equivalent behavior but this time with a dataclass. You'll have to bypass the frozen attribute:
from dataclasses import dataclass
@dataclass(frozen=True)
class WithString:
stringattr: str
def __init__(self, input_string):
object.__setattr__(self, "stringattr", input_string.lower())
string_1 = WithString("TEST")
string_2 = WithString("test")
print("instances:", string_1, string_2)
print("compare instances:", string_1 == string_2)
Output:
instances: WithString(stringattr='test') WithString(stringattr='test')
compare instances: True
Or just override str
The resulting object behaves a lot like a string and can also be compared to strings.
class CaseInsensitiveString(str):
def __eq__(self, other):
try:
return self.lower() == other.lower()
except AttributeError:
return NotImplemented
string_1 = CaseInsensitiveString("TEST")
string_2 = CaseInsensitiveString("test")
actual_string = "Test"
print("instances:", string_1, string_2)
print("compare instances:", string_1 == string_2)
print("compare left:", string_1 == actual_string)
print("compare right:", actual_string == string_1)
Output:
instances: TEST test
compare instances: True
compare left: True
compare right: True