5

actually, I already know what I want to do is kind of strange, but I think it will fit good in my code, so I'm asking:

is there a way to do something like this:

foo = { 'a':1, 'b':2, 'c':3 }
bar = { 'd':4, 'f':5, 'g':6 }

foo.get('h', bar.get('h'))

raising an exception instead of None, in case dict.get() 'fails'?

foo.get('h', bar.get('h', raise)) will raise SyntaxError

foo.get('h', bar.get('h', Exception)) will just return Exception

for now i'm just working around with if not foo.get('h', bar.get('h')): raise Exception but if there is a way to raise directly in the dict.get() I'd be very glad.

Thank you

lupodellasleppa
  • 124
  • 1
  • 1
  • 11
  • 1
    you could just subclass dict and make `get` do just what you want – Nullman Feb 21 '19 at 12:56
  • 1
    Don't use `get()` because it traps the IndexError for you. If you actually want the IndexError, just use `foo['h']` – BoarGules Feb 21 '19 at 12:56
  • 2
    The answers saying "just use brackets" are right, but here's an explanation as to why: https://stackoverflow.com/a/11041421/769971 Also, I promise that further down the road the `.get()` function will be useful to you. You should learn how to use that as well. – wholevinski Feb 21 '19 at 13:06
  • exactly, i know why .get() is used, I just wanted to raise an exception when the key is not found instead of `None`, without having to use conditionals – lupodellasleppa Feb 21 '19 at 14:06

7 Answers7

18

Using subscripts, this is the default behaviour:

d={}
d['unknown key'] --> Raises a KeyError

If you then want to throw a custom exception, you could do this:

try:
    d['unknown key']
except KeyError:
    raise CustomException('Custom message')

And to include the stacktrace from the KeyError:

try:
    d['unknown key']
except KeyError as e:
    raise CustomException('Custom message') from e
thom747
  • 805
  • 5
  • 18
  • Thanks. I already know about this, I wanted to use .get() so that I didn't have to use conditionals but in the end, looks like I'll just have to anyway – lupodellasleppa Feb 21 '19 at 14:05
12

If you want to raise error inside get then you can trick like this:

{"a":4}.get("b", exec("raise Exception('some error msg') "))

Also if you want to avoid typos then use f-strings.

Deepak Tripathi
  • 3,175
  • 1
  • 8
  • 21
  • 1
    Despite being the accepted answer and also a perfectly valid response to the question, I feel compelled to advise against using this in any sort of production code. See [this question](https://stackoverflow.com/questions/1933451/why-should-exec-and-eval-be-avoided) for some of the arguments against using exec in this way. – Robert Steward Feb 08 '23 at 14:44
  • @RobertSteward I agree with you but sometimes you have to do something wrong to get something right :) . – Deepak Tripathi Feb 08 '23 at 18:43
  • yeah I would not recommend using `exec()` in production code, instead you can just write `if/else` statement and throw an exception. – Yashashvi Feb 22 '23 at 01:37
  • check out this - https://stackoverflow.com/a/60465422/7768504 for custom exception – Yashashvi Feb 22 '23 at 01:41
  • @Yashashvi You can use `exec` in production code as well. However, you should avoid allowing it to be manipulated by input to prevent potential bugs. In the example where we provide static input to `exec`, there will be no issues. It's a common misconception that exec cannot be used in production, but as long as you use it with care and avoid security risks, it can be a useful tool. – Deepak Tripathi Feb 22 '23 at 05:35
2

You can do:

class MyException(Exception):
    pass


try:
    value = dict['h']
except KeyError:
    raise MyException('my message')
Sergey Pugach
  • 5,561
  • 1
  • 16
  • 31
1

since you already have some good answers i shall give you the boondoggle answer as a learning...thing.

class MyDict(dict):
    def get(self, key, default=None, error=None):
        res = super().get(key,default)
        if res is None:
            if error == 'raise':
                raise SyntaxError()
            elif error == 'Exception':
                return SyntaxError()
        return res

now you can do :

foo = MyDict({ 'a':1, 'b':2, 'c':3 })
bar = MyDict({ 'd':4, 'f':5, 'g':6 })
foo.get('h', bar.get('h', error="Exception")) #  returns a syntaxerror object
foo.get('h', bar.get('h', error="raise"))  # raises a syntax error

super() lets you access the members of your superclass so you can have your own get while still using the parents get internally

Nullman
  • 4,179
  • 2
  • 14
  • 30
0

You can do custom class for your dict with magic function :

class GetAndRaise:
    def __init__(self):
        self.dict = dict()
    def __getitem__(self, key):
        try:
            return self.dict[key]
        except ValueError:
            raise MyException
    def __setitem__(self, key, value):
        self.dict[key] = value
    def get(self, key):
        return self[key]
Akarys
  • 66
  • 5
  • 2
    I can't condone this...don't do this please, as the functionality he's looking for is already in the dict class. – wholevinski Feb 21 '19 at 13:07
0

You can use the container ChainMap, which encapsulates two dictionaries into one:

from collections import ChainMap

foo = { 'a':1, 'b':2, 'c':3 }
bar = { 'd':4, 'f':5, 'g':6 }

ChainMap(foo, bar)['h']
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
0

You could do some assert magic:

foo = { 'a':1, 'b':2, 'c':3 }
bar = { 'd':4, 'f':5, 'g':6 }

assert foo.get('h', bar.get('h')), 'value "h" did not exist in dicts!'

Foo tries to pick value of key "h" and fallbacks to bar picking value of key "h" if neither of these values return anything meaningful the default value of the innermost get call is returned, which in this case is None. None causes assertion to trigger.

This works because any object can be tested for truth value, like constants such as None. https://docs.python.org/3/library/stdtypes.html#truth-value-testing

Also you get the added benefit of customized error message.

For Python 3.8 + you could combine it with some walrus magic:

assert (myVariable := foo.get('h', bar.get('h'))), 'value "h" did not exist in dicts!'
# if value of key "h" was found from any of the dicts, it is now assigned to the variable myVariable.
patruski
  • 13
  • 3