maybe the default value should be None rather than deleted, so we don't need to use hasattr