Let us say I have a custom use case, and I need to dynamically create or define the __init__
method for a dataclass.
For exampel, say I will need to decorate it like @dataclass(init=False)
and then modify __init__()
method to taking keyword arguments, like **kwargs
. However, in the kwargs
object, I only check for presence of known dataclass fields, and set these attributes accordingly (example below)
I would like to type hint to my IDE (PyCharm) that the modified __init__
only accepts listed dataclass fields as parameters or keyword arguments. I am unsure if there is a way to approach this, using typing
library or otherwise. I know that PY3.11 has dataclass transforms planned, which may or may not do what I am looking for (my gut feeling is no).
Here is a sample code I was playing around with, which is a basic case which illustrates problem I am having:
from dataclasses import dataclass
# get value from input source (can be a file or anything else)
def get_value_from_src(_name: str, tp: type):
return tp() # dummy value
@dataclass
class MyClass:
foo: str
apple: int
def __init__(self, **kwargs):
for name, tp in self.__annotations__.items():
if name in kwargs:
value = kwargs[name]
else:
# here is where I would normally have the logic
# to read the value from another input source
value = get_value_from_src(name, tp)
if value is None:
raise ValueError
setattr(self, name, value)
c = MyClass(apple=None)
print(c)
c = MyClass(foo='bar', # here, I would like to auto-complete the name
# when I start typing `apple`
)
print(c)
If we assume that number or names of the fields are not fixed, I am curious if there could be a generic approach which would basically say to type checkers, "the __init__
of this class accepts only (optional) keyword arguments that match up on the fields defined in the dataclass itself".
Addendums, based on notes in comments below:
Passing
@dataclass(kw_only=True)
won't work because imagine I am writing this for a library, and need to support Python 3.7+. Also,kw_only
has no effect when a custom__init__()
is implemented, as in this case.The above is just a stub
__init__
method. it could have more complex logic, such as setting attributes based on a file source for example. basically the above is just a sample implementation of a larger use case.I can't update each field to
foo: Optional[str] = None
because that part would be implemented in user code, which I would not have any control over. Also, annotating it in this way doesn't make sense when you know a custom__init__()
method will be generated for you - meaning not bydataclasses
. Lastly, setting a default for each field just so that the class can be instantiated without arguments, likeMyClass()
, don't seem like the best idea to me.It would not work to let
dataclasses
auto-generate an__init__
, and instead implement a__post_init__()
. This would not work because I need to be able to construct the class without arguments, likeMyClass()
, as the field values will be set from another input source (think local file or elsewhere); this means that all fields would be required, so annotating them asOptional
would be fallacious in this case. I still need to be able to support user to enter optional keyword arguments, but these**kwargs
will always match up with dataclass field names, and so I desire some way for auto-completion to work with my IDE (PyCharm)
Hope this post clarifies the expectations and desired result. If there are any questions or anything that is a bit vague, please let me know.