Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How would you provide a no-argument called to manufacture default values #1

Closed
metaperl opened this issue Sep 1, 2020 · 6 comments
Closed
Labels
question Further information is requested

Comments

@metaperl
Copy link

metaperl commented Sep 1, 2020

In the docs, it stats that a copy is made of data items provided as defaults. But in core dataclasses, you can also supply a default_factory. How would you do this in dataclassy?

@biqqles
Copy link
Owner

biqqles commented Sep 1, 2020

Thanks for the question. As you pointed out, mutable defaults are copied, so for example list_field = field(default_factory=list) in dataclasses becomes simply list_field: List = [] with dataclassy. Or, as a more complicated example, dd_field: Dict = field(default_factory=lambda: defaultdict(list)) becomes dd_field: Dict = defaultdict(list).

I couldn't think of a use case this alternative mechanism wouldn't cover. Do you have one in mind?

@biqqles biqqles added the question Further information is requested label Sep 1, 2020
@metaperl
Copy link
Author

metaperl commented Sep 1, 2020

I couldn't think of a use case this alternative mechanism wouldn't cover. Do you have one in mind?

What if I have a field that will contain instances of MyCustomClass?

How would I specify this?

@biqqles
Copy link
Owner

biqqles commented Sep 1, 2020

If you define a copy method for the class it should work.

@biqqles
Copy link
Owner

biqqles commented Sep 3, 2020

I suppose this naturally raises the question of whether this is ideal. My thinking, as implied earlier, is that in the clear majority of cases where a default value can't be shared between instances it is for the reason that it is mutable, so copying it will suffice. In the case of collections, this is a clear advantage (imo) since it reduces the repetitiveness of lambdas. This would definitely be a disadvantage if you commonly wanted to create class instances, particularly if you wanted to create instances of classes you don't control.

Another option to the above solution would be to use post-initialisation logic:

@dataclass
class Example:
    mcc: MyCustomClass = None

    def __init__(self, mcc=None):
        self.mcc = MyCustomClass()

This has the major advantage of flexibility - you can do logic as complex as you like in there, including initialising MyCustomClass based on other fields of the dataclass or additional parameters to __init__. And of course, there is no need to define a copy method.

I admit that this is still not as "nice" as dataclasses' default_factory. However, adding a field clone would go almost violently against dataclassy's ethos of radical simplicity in its own codebase, especially as this is essentially the only parameter that there won't be a lighter-weight equivalent for. That's the reason it's not there already. I'm still very interested in hearing people's thoughts on this though.

@biqqles
Copy link
Owner

biqqles commented Sep 21, 2020

As of 03cebc9 this is now possible with

@dataclass
 class Example:
     mcc: MyCustomClass = None
 
     def __init__(self):
         self.mcc = MyCustomClass()

and an example has been added to the README to document this.

@biqqles biqqles closed this as completed Sep 21, 2020
@biqqles
Copy link
Owner

biqqles commented Apr 27, 2021

For future readers, as of release v0.9, this is now possible:

@dataclass
class Example:
    mcc: MyCustomClass = factory(MyCustomClass)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants