Rust's Default in Python
In Rust, I've used and liked quite a bit the Default trait, which lets you instantiate a struct without providing all the members, as a short example:
#[derive(Debug, Default)]
struct MyStruct {
value: i32,
}
fn main() {
let default_struct: MyStruct = Default::default();
println!("{:?}", default_struct); // Output: MyStruct { value: 0 }
}
A very important factor for me, is that this is recursive, as long as all members of each struct implement Default
.
I had not found a similar thing in Python, but today I learned about dacite, which, in combination with dataclasses' default_factory
can
implement something very similar.
import dacite
from dataclasses import dataclass, field
@dataclass
class Cron:
name: str
command: str
@dataclass
class BackupConfig:
enabled: bool
bucket: str
@staticmethod
def default() -> "BackupConfig":
return BackupConfig(enabled=False, bucket="default")
@dataclass
class HostConfig:
backup: BackupConfig
@staticmethod
def default() -> "HostConfig":
return HostConfig(
backup=BackupConfig.default(),
)
@dataclass
class HostData:
config: HostConfig = field(default_factory=HostConfig.default)
cron: list[Cron] = field(default_factory=list)
@staticmethod
def from_dict(data: dict) -> "HostData":
return dacite.from_dict(data_class=HostData, data=data, config=dacite.Config(strict=True))
print(HostData.from_dict({}))
print(HostData.from_dict({"cron": [{"name": "do something", "command": "ls"}]}))
Which outputs
HostData(config=HostConfig(backup=BackupConfig(enabled=False, bucket='default')), cron=[])
HostData(config=HostConfig(backup=BackupConfig(enabled=False, bucket='default')), cron=[Cron(name='do something', command='ls')])
This is very different, in my opinion, from adding default values to every property.
By having default values (enabled: bool = False
) it is possible to perform a partial instantiation, which, for me, is particularly troublesome when parsing user data -- I want the object to be entirely defined, or to entirely fall back to a default; I never want the object to be partially defined.
Default is also something you conciously implement; certain types do not have sane default values, such as BasicAuth(user, password)
or Cron
.
By using Type.default()
, you are being explicit about instantiating a default instance of an object.