Description
The idea is similar to attrs' converter functionality, although expanded. Often you need to convert one value to another, before assigning it to the field, but also without necessarily wanting to override any __init__ or __post_init__ methods.
A simple example of the syntax is:
@dataclass
class Character:
lit: str = field(init_using=chr)As shown, the new argument is init_using. It would take either a method name (this would indicate the method is defined on the class), or a callable, which could be say, a built-in method (like shown above).
In the example above, the field lit, still has the type str, however, in the __init__ method, rather than the type being str, the type is taken from the function signature of the init_using field, which in this example would be int (as the signature of chr is chr(number: int). The class would look something like this after all was generated:
class Character:
lit: str
def __init__(self, lit: int) -> None:
self.lit = chr(lit)The init_using parameter also allows you to init more than one field at a time. Below is an example:
@dataclass
class DocumentFile:
filename: str = field(init_using='_init_name_and_ctype')
content_type: str = field(init_using='_init_name_and_ctype')
def _init_name_and_ctype(self, filename: str | Path = '/tmp/example.txt') -> None:
self.filename = str(filename)
self.content_type = mimetypes.guess_type(filename)Passed to init_using is the method name of the method on the class. Any fields that pass a string to init_using are excluded from the __init__ function. Instead, the parameter names in the given method are used as the parameter names in the __init__ function.
Dataclass would then generate the given method for DocumentFile like so:
class DocumentFile:
filename: str
content_type: str
def __init__(self, filename: str | Path = '/tmp/example.txt'):
self._init_name_and_ctype(filename)
def _init_name_and_ctype(self, filename: str | Path = '/tmp/example.txt') -> None:
self.filename = str(filename)
self.content_type = mimetypes.guess_type(filename)the parameter names in the given method are used as the parameter names in the
__init__function.
To make that clearer, if you were to change the _init_name_and_ctype signature to be:
def _init_name_and_ctype(self, file: Path) -> None:
self.filename = str(file)
self.content_type = mimetypes.guess_type(file)The respective __init__ function would reflect that change and would generate like:
def __init__(self, file: Path):
self._init_name_and_ctype(file)The names of the fields do not have any affect on the init function when init_using is provided a string.
The argument default_factory would be mutually exclusive with init_using,. It hasn't yet been decided whether default is also mutually exclusive, or whether it adds the default value to the __init__ function like it already does.
Previous discussion
https://mail.python.org/archives/list/python-ideas@python.org/thread/4SM5EVP6MMGGHQMZSJXBML74PWWDHEWV/

