-1

My python program uses dicts and there are a massive amount of "if" statements just for checking the type of the retrieved value.

I want to avoid this but instead do it a more programatically correct way.

Here is an example:

# golddb should only contain str keys and int values
golddb = dict()

def gainGold(playername):
  global golddb
  golddb[playername] += 1  # error may happen if I try to += a non-int type
  golddb[playername] = "hello"  # I want python to give an error when I try to assign a str to be a value in the dict
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Ryan A WE
  • 59
  • 1
  • 10

3 Answers3

5

To verify that all keys/values of a dict are of a particular type you could use the all() function:

if all(isinstance(k, str) for k in playerdb):
    print("all keys are strs")

To enforce the types when storing values you could use a custom function to mediate access to the dictionary or, better yet, subclass dict and override the __setitem__ method, e.g.:

>>> class mydict(dict):
...     def __setitem__(self, key, val):
...         if not isinstance(key, str):
...             raise ValueError("key must be a str")
...         if not isinstance(val, int):
...             raise ValueError("value must be an int")
            dict.__setitem__(self, key, val)
...
>>> d = mydict()
>>> d["key"] = 1
>>> d["key"] = "value"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setitem__
ValueError: value must be an int
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • Very helpful and thanks! But how do I parase mydict as a dict into a JsonProperty? Will casting it work? "d as dict" https://cloud.google.com/appengine/docs/python/ndb/entity-property-reference – Ryan A WE May 18 '16 at 12:57
  • @Ryan: you can use `dict(d)` to get a `dict` instance, but since `mydict` **is-a** `dict`, you shouldn't be needing this – Eugene Yarmash May 18 '16 at 13:14
  • @eugeney Since mydict may define new members not present in dict, wouldn't casting mydict to dict be an issue? How does python deal with that? Ignore new members? – Ryan A WE May 18 '16 at 13:19
  • @eugeney http://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy If d contains many kvps, wouldn't dict(d) be a very expensive operation? Is "dict(d)" equivalent to "d as dict"? – Ryan A WE May 18 '16 at 14:11
  • @Ryan: `d as dict` is not valid syntax. Depending on the size of `d`, `dict(d)` might be expensive, but as I said you don't need copying - just use the instance of the subclass and it should work. Have you tried it? – Eugene Yarmash May 18 '16 at 14:18
  • I'm sorta busy with the server code at the moment, and will try mydict with JsonProperty after I'm finished, then I'll get back with the result. – Ryan A WE May 18 '16 at 15:58
  • Okay I've tested it. I set the JsonProperty to **mydict** and put() it in the datastore. However, after fetching it back, I printed the type() NDB returned, which showed up as a **dict**. (Also, the keys in the fetched dict turned from str to unicode.) – Ryan A WE May 18 '16 at 18:50
0

Python is not type safe. So the values stored in a dictionary can be of any type. One way to prevent values of other types to be added to the dictionary is to define a function which adds the data only if the type matches. Then only use this function to append to the dictionary.

def gain_gold(playername, amount):
    if isinstance(amount, int):
        playerdb[playername] = amount
    else:
       raise Exception('invalid type')
0

Updating this for Python >3.7. Python does not have type enforcements since it's a dynamically typed language and type enforcements can only happen in static languages like C++. Nonetheless, it has provided an option for type hinting. Don't mistake this for the run time execution to throw errors but at least the reader will know what to expect. If you want the program to throw an exception, you'll need something like what the posters have commented previously. Nonetheless, for type hinting for a dict item, you can use:

from typing import Dict

def fn(x) -> Dict[str,  int]:
     return {'foo': x}

The datatype Dict imported from typing provides context about the return statement. It tells you that this dictionary item has string key values and integer values associated with those key values.

gohar-fse
  • 11
  • 1
  • Note that there are also static type checking tools like [mypy](https://mypy-lang.org/) and [pyright](https://github.com/microsoft/pyright) that allow you to automatically check whether you violate the type annotations. – He3lixxx Aug 28 '23 at 22:28
  • Yes, I have been reading up on mypy. It's an additional step of course and unless you want to use it on GitHub actions for CI pipelines, it might be an overkill for most users. I have been using this for my prod env though and I agree, this is an amazing piece of addition to the python UX. – gohar-fse Aug 29 '23 at 11:22