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

Configure default value when key doesn't exist? #41

Closed
carmstrong opened this issue Jan 22, 2015 · 13 comments
Closed

Configure default value when key doesn't exist? #41

carmstrong opened this issue Jan 22, 2015 · 13 comments
Labels

Comments

@carmstrong
Copy link

Hi folks, great library! I really need the ability to specify a default value when I request a key that doesn't exist, instead of getting an empty Dict as a result. Would this be possible? I'm happy to contribute the code myself if this seems like something you'd want. None, empty string, or anything like that would be great. The other tools I'm using (mongoengine) are barfing because they don't know how to cast an empty Dict to any other type.

Thanks!

@mewwts
Copy link
Owner

mewwts commented Jan 22, 2015

Hi Chris!
Thanks for your input. You could call prune() and/or to_dict() before shipping it off to a module that doesn't handle the addict instances well.

Now, here's the problem we're facing with returning something but a Dict. In addict the key feature, for me at least, is the fact that you can create nested dictionaries using attributes.

>>> a = Dict()
>>> a.b.c.d.e = 2
>>> a
{'a': {'b': {'c': {'d': {'e': 2}}}}} 

This would not be possible (with the current implementation at least) if we were returning something else than an addict-instance, since when a unknown key is specified, we do not know if the intent was to set a value or getting one!

Does this sound OK to you?

@sabhiram
Copy link
Contributor

We can always expose another member function. Perhaps something to the effect of:

def get(self, key, default_value = None):
    ...

@carmstrong - How do you envision the specifying of said default value? Would it be dict wide? or unique to each key?

Perhaps when the dict is created we can tell it how to initialize default values for various types.

@carmstrong
Copy link
Author

Perhaps when the dict is created we can tell it how to initialize default values for various types.

Exactly! A dict-wide default would work just swell.

Your proposal is exactly what I'm envisioning. I really want to use Dict(), but I need default values to be sane Python. Using to_dict() isn't really possible for me because going this route in the first place was to avoid getting a KeyError when a key didn't exist.

@sabhiram
Copy link
Contributor

Hmm, I thought about this a bit more today. Here is another issue, as it stands today: we can legally do this with a Dict:

prop = Dict(a=2, b={'a': 2}, c=[{'a':2}])

So if we would try to set "str" to "DEFAULT" like so:

prop = Dict(a=2, b={'a': 2}, c=[{'a':2}], str="DEFAULT")

The code would just set a key called "str" to "DEFAULT" :)

@mewwts
Copy link
Owner

mewwts commented Jan 23, 2015

Well for what it's whort @sabhiram, you can use get today, as it belongs to the dict. So to get a default-value back, like you would with a normal dict-instance, you could simply do

>>>> my_dict.get('my_nonexistent_key', None)
None

Also, it is trivial to to chance the constructor signature to something like

def __init__(*args, default=None, **kwargs):

(The syntax might be off. I know it's different in 2.7 and 3.4)

@carmstrong, would you mind writing out an example for how you use this with mongoengine and when they fail using it? It would be useful to see.

@carmstrong
Copy link
Author

would you mind writing out an example for how you use this with mongoengine and when they fail using it? It would be useful to see.

Sure. So my issue is that I'm using xmltodict to convert a large XML payload to a dict. I then create a new dict with addict: Dict(xml). This works great, because I'm accessing deep member properties of the dict when I'm not sure their parent (or grandparent) even exists. For example:

my_dict.Result.Pet.Cat.Name

works great using addict, because if any of those sub-Dicts doesn't exist, I just get a {} back. With a regular dict, I'd get a KeyError which I don't want to have to try/except on every single call to the dict (there are dozens in this file). However, when using this with mongoengine, it doesn't know how to handle an empty Dict. Here's an example of how I'm using everything together:

    parsed = xmltodict.parse(xml)
    res = Dict(parsed)
    result = MyModel()

    errors = []
    for error in res.Results.Error:
      errors.append(ProcessingError(
        error_id = error['@id'],
        code = error.ErrorCode,
        message = error.ErrorMessage
      ))
    result.processing_errors = errors

In this example, ProcessingError is a mongoengine model:

class ProcessingError(EmbeddedDocument):
  error_id = IntField()
  code = StringField()
  message = StringField()

I'm getting an error when mongoengine tries to cast the error_id to an int, because in this case it's not provided, and is {}. mongoengine has no idea what a Dict is.

I hope that makes sense! Very much appreciate the replies.

@mewwts
Copy link
Owner

mewwts commented Jan 23, 2015

I'm away this weekend, so my responses might be few and short. Hope it wont hinder your progress!
Could this problem, for now, be bypassed by doing

errors = []
for error in res.Results.Error:
    errors.append(ProcessingError(
        error_id = error.get('@id', None),
        code = error.get('ErrorCode', None),
        message = error.get('ErrorMessage', None)
    ))

or am I wrong? Have a great weekend, and thanks for using, and contributing to addict!

@carmstrong
Copy link
Author

Could this problem, for now, be bypassed

You're right! I couldn't do that before I started using addict because some sub-dict may not exist and throw a KeyError, but now that I'm using a Dict I can just use .get on the final dict's key. Thanks for the idea!

@mewwts
Copy link
Owner

mewwts commented Jan 26, 2015

Great, @carmstrong!
Do you find that this is a good enough solution so that we may consider to close this issue?

@carmstrong
Copy link
Author

Do you find that this is a good enough solution so that we may consider to close this issue?

I still think it'd be nice to have a default method so I could just set it once on the parent Dict creation rather than the 100 or so times I'm accessing a sub-Dict, but I suppose that's up to you guys.

@mewwts
Copy link
Owner

mewwts commented Jan 28, 2015

Alright, @carmstrong, your usecase is a bit different from my typical one, and I see where you're coming from. I'll leave this open. I think it might be a candidate feature for future releases.

@perpetual-hydrofoil
Copy link

It'd be interesting to allow the Dict() constructor to accept a param that would allow any arbitrary class (or perhaps a function that'd return some value or object) to serve as a default for unset values.

@mewwts
Copy link
Owner

mewwts commented Jul 15, 2016

Closing this as there has been no activity for a long time.

@mewwts mewwts closed this as completed Jul 15, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants