You're right that consistent internal state is important, but you can accomplish this with
class MyClass:
def __init__(self, config: str):
self._config = config
And if your reaction is "that just means something else needs to call Path("config.json").read_text()", you're absolutely right! It's separation of concerns; let some other method deal with the possibility that `config.json` isn't accessible. In the real world, you'd presumably want even more specific checks that specific config values were present in the JSON file, and your constructor would look more like
def __init__(self, host: str, port: int):
and you'd validate that those values are present in the same place that loads the JSON.
This simple change makes code so much more readable and testable.
This simple change makes code so much more readable and testable.